diff --git a/migrations/app/migrations_manifest.txt b/migrations/app/migrations_manifest.txt index 2638419bdd6..a3fd56d6e72 100644 --- a/migrations/app/migrations_manifest.txt +++ b/migrations/app/migrations_manifest.txt @@ -1037,4 +1037,5 @@ 20241107180705_add_alternative_AK_HI_duty_location_names.up.sql 20241109002854_add_gsr_table_to_move_history.up.sql 20241111203514_add_external_crate_and_remove_icrtsa.up.sql +20241111221400_add_new_order_types.up.sql 20241111223224_change_international_sit_services_to_accessorials.up.sql diff --git a/migrations/app/schema/20241111221400_add_new_order_types.up.sql b/migrations/app/schema/20241111221400_add_new_order_types.up.sql new file mode 100644 index 00000000000..efbb9f81208 --- /dev/null +++ b/migrations/app/schema/20241111221400_add_new_order_types.up.sql @@ -0,0 +1,5 @@ +-- adding order types to orders_type enum used in the orders table. +ALTER TYPE public.orders_type ADD VALUE 'EARLY_RETURN_OF_DEPENDENTS'; +ALTER TYPE public.orders_type ADD VALUE 'STUDENT_TRAVEL'; +COMMENT ON COLUMN orders.orders_type IS 'MilMove supports 10 orders types: Permanent change of station (PCS), local move, retirement, separation, wounded warrior, bluebark, safety, temporary duty (TDY), early return of dependents, and student travel. +In general, the moving process starts with the job/travel orders a customer receives from their service. In the orders, information describing rank, the duration of job/training, and their assigned location will determine if their entire dependent family can come, what the customer is allowed to bring, and how those items will arrive to their new location.' diff --git a/pkg/gen/ghcapi/embedded_spec.go b/pkg/gen/ghcapi/embedded_spec.go index d18943299eb..e3d89214311 100644 --- a/pkg/gen/ghcapi/embedded_spec.go +++ b/pkg/gen/ghcapi/embedded_spec.go @@ -10955,15 +10955,19 @@ func init() { "WOUNDED_WARRIOR", "BLUEBARK", "SAFETY", - "TEMPORARY_DUTY" + "TEMPORARY_DUTY", + "EARLY_RETURN_OF_DEPENDENTS", + "STUDENT_TRAVEL" ], "x-display-value": { "BLUEBARK": "BLUEBARK", + "EARLY_RETURN_OF_DEPENDENTS": "Early Return of Dependents", "LOCAL_MOVE": "Local Move", "PERMANENT_CHANGE_OF_STATION": "Permanent Change Of Station", "RETIREMENT": "Retirement", "SAFETY": "Safety", "SEPARATION": "Separation", + "STUDENT_TRAVEL": "Student Travel", "TEMPORARY_DUTY": "Temporary Duty (TDY)", "WOUNDED_WARRIOR": "Wounded Warrior" } @@ -27467,15 +27471,19 @@ func init() { "WOUNDED_WARRIOR", "BLUEBARK", "SAFETY", - "TEMPORARY_DUTY" + "TEMPORARY_DUTY", + "EARLY_RETURN_OF_DEPENDENTS", + "STUDENT_TRAVEL" ], "x-display-value": { "BLUEBARK": "BLUEBARK", + "EARLY_RETURN_OF_DEPENDENTS": "Early Return of Dependents", "LOCAL_MOVE": "Local Move", "PERMANENT_CHANGE_OF_STATION": "Permanent Change Of Station", "RETIREMENT": "Retirement", "SAFETY": "Safety", "SEPARATION": "Separation", + "STUDENT_TRAVEL": "Student Travel", "TEMPORARY_DUTY": "Temporary Duty (TDY)", "WOUNDED_WARRIOR": "Wounded Warrior" } diff --git a/pkg/gen/ghcmessages/orders_type.go b/pkg/gen/ghcmessages/orders_type.go index a578f540afe..13ed0b535c1 100644 --- a/pkg/gen/ghcmessages/orders_type.go +++ b/pkg/gen/ghcmessages/orders_type.go @@ -53,6 +53,12 @@ const ( // OrdersTypeTEMPORARYDUTY captures enum value "TEMPORARY_DUTY" OrdersTypeTEMPORARYDUTY OrdersType = "TEMPORARY_DUTY" + + // OrdersTypeEARLYRETURNOFDEPENDENTS captures enum value "EARLY_RETURN_OF_DEPENDENTS" + OrdersTypeEARLYRETURNOFDEPENDENTS OrdersType = "EARLY_RETURN_OF_DEPENDENTS" + + // OrdersTypeSTUDENTTRAVEL captures enum value "STUDENT_TRAVEL" + OrdersTypeSTUDENTTRAVEL OrdersType = "STUDENT_TRAVEL" ) // for schema @@ -60,7 +66,7 @@ var ordersTypeEnum []interface{} func init() { var res []OrdersType - if err := json.Unmarshal([]byte(`["PERMANENT_CHANGE_OF_STATION","LOCAL_MOVE","RETIREMENT","SEPARATION","WOUNDED_WARRIOR","BLUEBARK","SAFETY","TEMPORARY_DUTY"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["PERMANENT_CHANGE_OF_STATION","LOCAL_MOVE","RETIREMENT","SEPARATION","WOUNDED_WARRIOR","BLUEBARK","SAFETY","TEMPORARY_DUTY","EARLY_RETURN_OF_DEPENDENTS","STUDENT_TRAVEL"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/pkg/gen/internalapi/embedded_spec.go b/pkg/gen/internalapi/embedded_spec.go index 2d0cb0110cf..059743875ec 100644 --- a/pkg/gen/internalapi/embedded_spec.go +++ b/pkg/gen/internalapi/embedded_spec.go @@ -6017,15 +6017,19 @@ func init() { "WOUNDED_WARRIOR", "BLUEBARK", "SAFETY", - "TEMPORARY_DUTY" + "TEMPORARY_DUTY", + "EARLY_RETURN_OF_DEPENDENTS", + "STUDENT_TRAVEL" ], "x-display-value": { "BLUEBARK": "BLUEBARK", + "EARLY_RETURN_OF_DEPENDENTS": "Early Return of Dependents", "LOCAL_MOVE": "Local Move", "PERMANENT_CHANGE_OF_STATION": "Permanent Change Of Station", "RETIREMENT": "Retirement", "SAFETY": "Safety", "SEPARATION": "Separation", + "STUDENT_TRAVEL": "Student Travel", "TEMPORARY_DUTY": "Temporary Duty (TDY)", "WOUNDED_WARRIOR": "Wounded Warrior" } @@ -14888,15 +14892,19 @@ func init() { "WOUNDED_WARRIOR", "BLUEBARK", "SAFETY", - "TEMPORARY_DUTY" + "TEMPORARY_DUTY", + "EARLY_RETURN_OF_DEPENDENTS", + "STUDENT_TRAVEL" ], "x-display-value": { "BLUEBARK": "BLUEBARK", + "EARLY_RETURN_OF_DEPENDENTS": "Early Return of Dependents", "LOCAL_MOVE": "Local Move", "PERMANENT_CHANGE_OF_STATION": "Permanent Change Of Station", "RETIREMENT": "Retirement", "SAFETY": "Safety", "SEPARATION": "Separation", + "STUDENT_TRAVEL": "Student Travel", "TEMPORARY_DUTY": "Temporary Duty (TDY)", "WOUNDED_WARRIOR": "Wounded Warrior" } diff --git a/pkg/gen/internalmessages/orders_type.go b/pkg/gen/internalmessages/orders_type.go index 043ebf02297..059e20ff623 100644 --- a/pkg/gen/internalmessages/orders_type.go +++ b/pkg/gen/internalmessages/orders_type.go @@ -53,6 +53,12 @@ const ( // OrdersTypeTEMPORARYDUTY captures enum value "TEMPORARY_DUTY" OrdersTypeTEMPORARYDUTY OrdersType = "TEMPORARY_DUTY" + + // OrdersTypeEARLYRETURNOFDEPENDENTS captures enum value "EARLY_RETURN_OF_DEPENDENTS" + OrdersTypeEARLYRETURNOFDEPENDENTS OrdersType = "EARLY_RETURN_OF_DEPENDENTS" + + // OrdersTypeSTUDENTTRAVEL captures enum value "STUDENT_TRAVEL" + OrdersTypeSTUDENTTRAVEL OrdersType = "STUDENT_TRAVEL" ) // for schema @@ -60,7 +66,7 @@ var ordersTypeEnum []interface{} func init() { var res []OrdersType - if err := json.Unmarshal([]byte(`["PERMANENT_CHANGE_OF_STATION","LOCAL_MOVE","RETIREMENT","SEPARATION","WOUNDED_WARRIOR","BLUEBARK","SAFETY","TEMPORARY_DUTY"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["PERMANENT_CHANGE_OF_STATION","LOCAL_MOVE","RETIREMENT","SEPARATION","WOUNDED_WARRIOR","BLUEBARK","SAFETY","TEMPORARY_DUTY","EARLY_RETURN_OF_DEPENDENTS","STUDENT_TRAVEL"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/pkg/gen/primeapi/embedded_spec.go b/pkg/gen/primeapi/embedded_spec.go index 31cc53c4578..01eaf7b26ae 100644 --- a/pkg/gen/primeapi/embedded_spec.go +++ b/pkg/gen/primeapi/embedded_spec.go @@ -2970,15 +2970,19 @@ func init() { "WOUNDED_WARRIOR", "BLUEBARK", "SAFETY", - "TEMPORARY_DUTY" + "TEMPORARY_DUTY", + "EARLY_RETURN_OF_DEPENDENTS", + "STUDENT_TRAVEL" ], "x-display-value": { "BLUEBARK": "BLUEBARK", + "EARLY_RETURN_OF_DEPENDENTS": "Early Return of Dependents", "LOCAL_MOVE": "Local Move", "PERMANENT_CHANGE_OF_STATION": "Permanent Change Of Station", "RETIREMENT": "Retirement", "SAFETY": "Safety", "SEPARATION": "Separation", + "STUDENT_TRAVEL": "Student Travel", "TEMPORARY_DUTY": "Temporary Duty (TDY)", "WOUNDED_WARRIOR": "Wounded Warrior" } @@ -7793,15 +7797,19 @@ func init() { "WOUNDED_WARRIOR", "BLUEBARK", "SAFETY", - "TEMPORARY_DUTY" + "TEMPORARY_DUTY", + "EARLY_RETURN_OF_DEPENDENTS", + "STUDENT_TRAVEL" ], "x-display-value": { "BLUEBARK": "BLUEBARK", + "EARLY_RETURN_OF_DEPENDENTS": "Early Return of Dependents", "LOCAL_MOVE": "Local Move", "PERMANENT_CHANGE_OF_STATION": "Permanent Change Of Station", "RETIREMENT": "Retirement", "SAFETY": "Safety", "SEPARATION": "Separation", + "STUDENT_TRAVEL": "Student Travel", "TEMPORARY_DUTY": "Temporary Duty (TDY)", "WOUNDED_WARRIOR": "Wounded Warrior" } diff --git a/pkg/gen/primemessages/orders_type.go b/pkg/gen/primemessages/orders_type.go index 8128616b85b..84f9bf262bc 100644 --- a/pkg/gen/primemessages/orders_type.go +++ b/pkg/gen/primemessages/orders_type.go @@ -53,6 +53,12 @@ const ( // OrdersTypeTEMPORARYDUTY captures enum value "TEMPORARY_DUTY" OrdersTypeTEMPORARYDUTY OrdersType = "TEMPORARY_DUTY" + + // OrdersTypeEARLYRETURNOFDEPENDENTS captures enum value "EARLY_RETURN_OF_DEPENDENTS" + OrdersTypeEARLYRETURNOFDEPENDENTS OrdersType = "EARLY_RETURN_OF_DEPENDENTS" + + // OrdersTypeSTUDENTTRAVEL captures enum value "STUDENT_TRAVEL" + OrdersTypeSTUDENTTRAVEL OrdersType = "STUDENT_TRAVEL" ) // for schema @@ -60,7 +66,7 @@ var ordersTypeEnum []interface{} func init() { var res []OrdersType - if err := json.Unmarshal([]byte(`["PERMANENT_CHANGE_OF_STATION","LOCAL_MOVE","RETIREMENT","SEPARATION","WOUNDED_WARRIOR","BLUEBARK","SAFETY","TEMPORARY_DUTY"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["PERMANENT_CHANGE_OF_STATION","LOCAL_MOVE","RETIREMENT","SEPARATION","WOUNDED_WARRIOR","BLUEBARK","SAFETY","TEMPORARY_DUTY","EARLY_RETURN_OF_DEPENDENTS","STUDENT_TRAVEL"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/pkg/gen/primev2api/embedded_spec.go b/pkg/gen/primev2api/embedded_spec.go index bc5f0b26372..95f4d7137c6 100644 --- a/pkg/gen/primev2api/embedded_spec.go +++ b/pkg/gen/primev2api/embedded_spec.go @@ -2121,15 +2121,19 @@ func init() { "WOUNDED_WARRIOR", "BLUEBARK", "SAFETY", - "TEMPORARY_DUTY" + "TEMPORARY_DUTY", + "EARLY_RETURN_OF_DEPENDENTS", + "STUDENT_TRAVEL" ], "x-display-value": { "BLUEBARK": "BLUEBARK", + "EARLY_RETURN_OF_DEPENDENTS": "Early Return of Dependents", "LOCAL_MOVE": "Local Move", "PERMANENT_CHANGE_OF_STATION": "Permanent Change Of Station", "RETIREMENT": "Retirement", "SAFETY": "Safety", "SEPARATION": "Separation", + "STUDENT_TRAVEL": "Student Travel", "TEMPORARY_DUTY": "Temporary Duty (TDY)", "WOUNDED_WARRIOR": "Wounded Warrior" } @@ -5711,15 +5715,19 @@ func init() { "WOUNDED_WARRIOR", "BLUEBARK", "SAFETY", - "TEMPORARY_DUTY" + "TEMPORARY_DUTY", + "EARLY_RETURN_OF_DEPENDENTS", + "STUDENT_TRAVEL" ], "x-display-value": { "BLUEBARK": "BLUEBARK", + "EARLY_RETURN_OF_DEPENDENTS": "Early Return of Dependents", "LOCAL_MOVE": "Local Move", "PERMANENT_CHANGE_OF_STATION": "Permanent Change Of Station", "RETIREMENT": "Retirement", "SAFETY": "Safety", "SEPARATION": "Separation", + "STUDENT_TRAVEL": "Student Travel", "TEMPORARY_DUTY": "Temporary Duty (TDY)", "WOUNDED_WARRIOR": "Wounded Warrior" } diff --git a/pkg/gen/primev2messages/orders_type.go b/pkg/gen/primev2messages/orders_type.go index a4b2330c915..a5f02303750 100644 --- a/pkg/gen/primev2messages/orders_type.go +++ b/pkg/gen/primev2messages/orders_type.go @@ -53,6 +53,12 @@ const ( // OrdersTypeTEMPORARYDUTY captures enum value "TEMPORARY_DUTY" OrdersTypeTEMPORARYDUTY OrdersType = "TEMPORARY_DUTY" + + // OrdersTypeEARLYRETURNOFDEPENDENTS captures enum value "EARLY_RETURN_OF_DEPENDENTS" + OrdersTypeEARLYRETURNOFDEPENDENTS OrdersType = "EARLY_RETURN_OF_DEPENDENTS" + + // OrdersTypeSTUDENTTRAVEL captures enum value "STUDENT_TRAVEL" + OrdersTypeSTUDENTTRAVEL OrdersType = "STUDENT_TRAVEL" ) // for schema @@ -60,7 +66,7 @@ var ordersTypeEnum []interface{} func init() { var res []OrdersType - if err := json.Unmarshal([]byte(`["PERMANENT_CHANGE_OF_STATION","LOCAL_MOVE","RETIREMENT","SEPARATION","WOUNDED_WARRIOR","BLUEBARK","SAFETY","TEMPORARY_DUTY"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["PERMANENT_CHANGE_OF_STATION","LOCAL_MOVE","RETIREMENT","SEPARATION","WOUNDED_WARRIOR","BLUEBARK","SAFETY","TEMPORARY_DUTY","EARLY_RETURN_OF_DEPENDENTS","STUDENT_TRAVEL"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/pkg/gen/primev3api/embedded_spec.go b/pkg/gen/primev3api/embedded_spec.go index 57b4a78d6e2..15fc2ad7eef 100644 --- a/pkg/gen/primev3api/embedded_spec.go +++ b/pkg/gen/primev3api/embedded_spec.go @@ -2349,15 +2349,19 @@ func init() { "WOUNDED_WARRIOR", "BLUEBARK", "SAFETY", - "TEMPORARY_DUTY" + "TEMPORARY_DUTY", + "EARLY_RETURN_OF_DEPENDENTS", + "STUDENT_TRAVEL" ], "x-display-value": { "BLUEBARK": "BLUEBARK", + "EARLY_RETURN_OF_DEPENDENTS": "Early Return of Dependents", "LOCAL_MOVE": "Local Move", "PERMANENT_CHANGE_OF_STATION": "Permanent Change Of Station", "RETIREMENT": "Retirement", "SAFETY": "Safety", "SEPARATION": "Separation", + "STUDENT_TRAVEL": "Student Travel", "TEMPORARY_DUTY": "Temporary Duty (TDY)", "WOUNDED_WARRIOR": "Wounded Warrior" } @@ -6498,15 +6502,19 @@ func init() { "WOUNDED_WARRIOR", "BLUEBARK", "SAFETY", - "TEMPORARY_DUTY" + "TEMPORARY_DUTY", + "EARLY_RETURN_OF_DEPENDENTS", + "STUDENT_TRAVEL" ], "x-display-value": { "BLUEBARK": "BLUEBARK", + "EARLY_RETURN_OF_DEPENDENTS": "Early Return of Dependents", "LOCAL_MOVE": "Local Move", "PERMANENT_CHANGE_OF_STATION": "Permanent Change Of Station", "RETIREMENT": "Retirement", "SAFETY": "Safety", "SEPARATION": "Separation", + "STUDENT_TRAVEL": "Student Travel", "TEMPORARY_DUTY": "Temporary Duty (TDY)", "WOUNDED_WARRIOR": "Wounded Warrior" } diff --git a/pkg/gen/primev3messages/orders_type.go b/pkg/gen/primev3messages/orders_type.go index dfe1b6a549e..ee3a1d78344 100644 --- a/pkg/gen/primev3messages/orders_type.go +++ b/pkg/gen/primev3messages/orders_type.go @@ -53,6 +53,12 @@ const ( // OrdersTypeTEMPORARYDUTY captures enum value "TEMPORARY_DUTY" OrdersTypeTEMPORARYDUTY OrdersType = "TEMPORARY_DUTY" + + // OrdersTypeEARLYRETURNOFDEPENDENTS captures enum value "EARLY_RETURN_OF_DEPENDENTS" + OrdersTypeEARLYRETURNOFDEPENDENTS OrdersType = "EARLY_RETURN_OF_DEPENDENTS" + + // OrdersTypeSTUDENTTRAVEL captures enum value "STUDENT_TRAVEL" + OrdersTypeSTUDENTTRAVEL OrdersType = "STUDENT_TRAVEL" ) // for schema @@ -60,7 +66,7 @@ var ordersTypeEnum []interface{} func init() { var res []OrdersType - if err := json.Unmarshal([]byte(`["PERMANENT_CHANGE_OF_STATION","LOCAL_MOVE","RETIREMENT","SEPARATION","WOUNDED_WARRIOR","BLUEBARK","SAFETY","TEMPORARY_DUTY"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["PERMANENT_CHANGE_OF_STATION","LOCAL_MOVE","RETIREMENT","SEPARATION","WOUNDED_WARRIOR","BLUEBARK","SAFETY","TEMPORARY_DUTY","EARLY_RETURN_OF_DEPENDENTS","STUDENT_TRAVEL"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/pkg/gen/supportapi/embedded_spec.go b/pkg/gen/supportapi/embedded_spec.go index 11683bc1e34..31eea1917ec 100644 --- a/pkg/gen/supportapi/embedded_spec.go +++ b/pkg/gen/supportapi/embedded_spec.go @@ -2074,16 +2074,20 @@ func init() { "NTS", "WOUNDED_WARRIOR", "BLUEBARK", - "TEMPORARY_DUTY" + "TEMPORARY_DUTY", + "EARLY_RETURN_OF_DEPENDENTS", + "STUDENT_TRAVEL" ], "x-display-value": { "BLUEBARK": "BLUEBARK", + "EARLY_RETURN_OF_DEPENDENTS": "Early Return of Dependents", "GHC": "GHC", "LOCAL_MOVE": "Local Move", "NTS": "NTS", "PERMANENT_CHANGE_OF_STATION": "Permanent Change Of Station (PCS)", "RETIREMENT": "Retirement", "SEPARATION": "Separation", + "STUDENT_TRAVEL": "Student Travel", "TEMPORARY_DUTY": "Temporary Duty (TDY)", "WOUNDED_WARRIOR": "Wounded Warrior" } @@ -4937,16 +4941,20 @@ func init() { "NTS", "WOUNDED_WARRIOR", "BLUEBARK", - "TEMPORARY_DUTY" + "TEMPORARY_DUTY", + "EARLY_RETURN_OF_DEPENDENTS", + "STUDENT_TRAVEL" ], "x-display-value": { "BLUEBARK": "BLUEBARK", + "EARLY_RETURN_OF_DEPENDENTS": "Early Return of Dependents", "GHC": "GHC", "LOCAL_MOVE": "Local Move", "NTS": "NTS", "PERMANENT_CHANGE_OF_STATION": "Permanent Change Of Station (PCS)", "RETIREMENT": "Retirement", "SEPARATION": "Separation", + "STUDENT_TRAVEL": "Student Travel", "TEMPORARY_DUTY": "Temporary Duty (TDY)", "WOUNDED_WARRIOR": "Wounded Warrior" } diff --git a/pkg/gen/supportmessages/orders_type.go b/pkg/gen/supportmessages/orders_type.go index 24fb04b2d53..39dedfe3064 100644 --- a/pkg/gen/supportmessages/orders_type.go +++ b/pkg/gen/supportmessages/orders_type.go @@ -56,6 +56,12 @@ const ( // OrdersTypeTEMPORARYDUTY captures enum value "TEMPORARY_DUTY" OrdersTypeTEMPORARYDUTY OrdersType = "TEMPORARY_DUTY" + + // OrdersTypeEARLYRETURNOFDEPENDENTS captures enum value "EARLY_RETURN_OF_DEPENDENTS" + OrdersTypeEARLYRETURNOFDEPENDENTS OrdersType = "EARLY_RETURN_OF_DEPENDENTS" + + // OrdersTypeSTUDENTTRAVEL captures enum value "STUDENT_TRAVEL" + OrdersTypeSTUDENTTRAVEL OrdersType = "STUDENT_TRAVEL" ) // for schema @@ -63,7 +69,7 @@ var ordersTypeEnum []interface{} func init() { var res []OrdersType - if err := json.Unmarshal([]byte(`["PERMANENT_CHANGE_OF_STATION","LOCAL_MOVE","RETIREMENT","SEPARATION","GHC","NTS","WOUNDED_WARRIOR","BLUEBARK","TEMPORARY_DUTY"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["PERMANENT_CHANGE_OF_STATION","LOCAL_MOVE","RETIREMENT","SEPARATION","GHC","NTS","WOUNDED_WARRIOR","BLUEBARK","TEMPORARY_DUTY","EARLY_RETURN_OF_DEPENDENTS","STUDENT_TRAVEL"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/src/components/Customer/EditOrdersForm/EditOrdersForm.jsx b/src/components/Customer/EditOrdersForm/EditOrdersForm.jsx index 1d7e55768af..c2616499fcf 100644 --- a/src/components/Customer/EditOrdersForm/EditOrdersForm.jsx +++ b/src/components/Customer/EditOrdersForm/EditOrdersForm.jsx @@ -10,7 +10,7 @@ import styles from './EditOrdersForm.module.scss'; import MaskedTextField from 'components/form/fields/MaskedTextField/MaskedTextField'; import ToolTip from 'shared/ToolTip/ToolTip'; -import { ORDERS_PAY_GRADE_OPTIONS } from 'constants/orders'; +import { ORDERS_PAY_GRADE_OPTIONS, ORDERS_TYPE } from 'constants/orders'; import { Form } from 'components/form/Form'; import FileUpload from 'components/FileUpload/FileUpload'; import UploadsTable from 'components/UploadsTable/UploadsTable'; @@ -48,6 +48,12 @@ const EditOrdersForm = ({ const [isLoading, setIsLoading] = useState(true); const [finishedFetchingFF, setFinishedFetchingFF] = useState(false); + const isInitialHasDependentsDisabled = + initialValues.orders_type === ORDERS_TYPE.STUDENT_TRAVEL || + initialValues.orders_type === ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS; + const [isHasDependentsDisabled, setHasDependentsDisabled] = useState(isInitialHasDependentsDisabled); + const [prevOrderType, setPrevOrderType] = useState(initialValues.orders_type); + const validationSchema = Yup.object().shape({ orders_type: Yup.mixed() .oneOf(ordersTypeOptions.map((i) => i.key)) @@ -148,7 +154,10 @@ const EditOrdersForm = ({ return ( - {({ isValid, isSubmitting, handleSubmit, values, setFieldValue }) => { + {({ isValid, isSubmitting, handleSubmit, handleChange, values, setFieldValue }) => { const isRetirementOrSeparation = ['RETIREMENT', 'SEPARATION'].includes(values.orders_type); if (!values.origin_duty_location) originMeta = 'Required'; @@ -177,16 +186,37 @@ const EditOrdersForm = ({ const handleHasDependentsChange = (e) => { // Declare a duplicate local scope of the field value // for the form to prevent state race conditions - const fieldValueHasDependents = e.target.value === 'yes'; - setHasDependents(fieldValueHasDependents); - setFieldValue('has_dependents', fieldValueHasDependents ? 'yes' : 'no'); - if (fieldValueHasDependents && isOconusMove && enableUB) { - setShowAccompaniedTourField(true); - setShowDependentAgeFields(true); + if (e.target.value === '') { + setFieldValue('has_dependents', ''); + } else { + const fieldValueHasDependents = e.target.value === 'yes'; + setHasDependents(fieldValueHasDependents); + setFieldValue('has_dependents', fieldValueHasDependents ? 'yes' : 'no'); + if (fieldValueHasDependents && isOconusMove && enableUB) { + setShowAccompaniedTourField(true); + setShowDependentAgeFields(true); + } else { + setShowAccompaniedTourField(false); + setShowDependentAgeFields(false); + } + } + }; + + const handleOrderTypeChange = (e) => { + const { value } = e.target; + if (value === ORDERS_TYPE.STUDENT_TRAVEL || value === ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS) { + setHasDependentsDisabled(true); + handleHasDependentsChange({ target: { value: 'yes' } }); } else { - setShowAccompaniedTourField(false); - setShowDependentAgeFields(false); + setHasDependentsDisabled(false); + if ( + prevOrderType === ORDERS_TYPE.STUDENT_TRAVEL || + prevOrderType === ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS + ) { + handleHasDependentsChange({ target: { value: '' } }); + } } + setPrevOrderType(value); }; return ( @@ -210,6 +240,10 @@ const EditOrdersForm = ({ options={ordersTypeOptions} required hint="Required" + onChange={(e) => { + handleChange(e); + handleOrderTypeChange(e); + }} /> - { handleHasDependentsChange(e); }} + disabled={isHasDependentsDisabled} /> { handleHasDependentsChange(e); }} + disabled={isHasDependentsDisabled} /> diff --git a/src/components/Customer/EditOrdersForm/EditOrdersForm.test.jsx b/src/components/Customer/EditOrdersForm/EditOrdersForm.test.jsx index 8747314b143..3d3bebbac2a 100644 --- a/src/components/Customer/EditOrdersForm/EditOrdersForm.test.jsx +++ b/src/components/Customer/EditOrdersForm/EditOrdersForm.test.jsx @@ -2,11 +2,13 @@ import React from 'react'; import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; +import { isBooleanFlagEnabled } from '../../../utils/featureFlags'; + import EditOrdersForm from './EditOrdersForm'; import { documentSizeLimitMsg } from 'shared/constants'; import { showCounselingOffices } from 'services/internalApi'; -import { ORDERS_TYPE } from 'constants/orders'; +import { ORDERS_TYPE, ORDERS_TYPE_OPTIONS } from 'constants/orders'; jest.setTimeout(60000); @@ -152,6 +154,10 @@ jest.mock('components/LocationSearchBox/api', () => ({ ), })); +jest.mock('../../../utils/featureFlags', () => ({ + isBooleanFlagEnabled: jest.fn(), +})); + const testProps = { onSubmit: jest.fn().mockImplementation(() => Promise.resolve()), initialValues: { @@ -176,6 +182,8 @@ const testProps = { { key: 'RETIREMENT', value: 'Retirement' }, { key: 'SEPARATION', value: 'Separation' }, { key: 'TEMPORARY_DUTY', value: 'Temporary Duty (TDY)' }, + { key: ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS, value: ORDERS_TYPE_OPTIONS.EARLY_RETURN_OF_DEPENDENTS }, + { key: ORDERS_TYPE.STUDENT_TRAVEL, value: ORDERS_TYPE_OPTIONS.STUDENT_TRAVEL }, ], currentDutyLocation: {}, grade: '', @@ -280,7 +288,11 @@ describe('EditOrdersForm component', () => { ['RETIREMENT', 'RETIREMENT'], ['SEPARATION', 'SEPARATION'], ['TEMPORARY_DUTY', 'TEMPORARY_DUTY'], + [ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS, ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS], + [ORDERS_TYPE.STUDENT_TRAVEL, ORDERS_TYPE.STUDENT_TRAVEL], ])('rendering the %s option', async (selectionOption, expectedValue) => { + isBooleanFlagEnabled.mockImplementation(() => Promise.resolve(true)); + render(); const ordersTypeDropdown = await screen.findByLabelText(/Orders type/); @@ -607,6 +619,8 @@ describe('EditOrdersForm component', () => { { key: 'RETIREMENT', value: 'Retirement' }, { key: 'SEPARATION', value: 'Separation' }, { key: 'TEMPORARY_DUTY', value: 'Temporary Duty (TDY)' }, + { key: ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS, value: ORDERS_TYPE_OPTIONS.EARLY_RETURN_OF_DEPENDENTS }, + { key: ORDERS_TYPE.STUDENT_TRAVEL, value: ORDERS_TYPE_OPTIONS.STUDENT_TRAVEL }, ], currentDutyLocation: {}, }; @@ -687,5 +701,111 @@ describe('EditOrdersForm component', () => { }); }); + it('has dependents is yes and disabled when order type is student travel', async () => { + isBooleanFlagEnabled.mockImplementation(() => Promise.resolve(true)); + + render(); + + await waitFor(() => expect(screen.queryByText('Loading, please wait...')).not.toBeInTheDocument()); + + await userEvent.selectOptions(screen.getByLabelText(/Orders type/), ORDERS_TYPE.STUDENT_TRAVEL); + + const hasDependentsYes = screen.getByLabelText('Yes'); + const hasDependentsNo = screen.getByLabelText('No'); + + await waitFor(() => { + expect(hasDependentsYes).toBeChecked(); + expect(hasDependentsYes).toBeDisabled(); + expect(hasDependentsNo).toBeDisabled(); + }); + }); + + it('has dependents is yes and disabled when order type is early return', async () => { + render(); + + await waitFor(() => expect(screen.queryByText('Loading, please wait...')).not.toBeInTheDocument()); + + await userEvent.selectOptions(screen.getByLabelText(/Orders type/), ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS); + + const hasDependentsYes = screen.getByLabelText('Yes'); + const hasDependentsNo = screen.getByLabelText('No'); + + await waitFor(() => { + expect(hasDependentsYes).toBeChecked(); + expect(hasDependentsYes).toBeDisabled(); + expect(hasDependentsNo).toBeDisabled(); + }); + }); + + it('has dependents becomes disabled and then re-enabled for order type student travel', async () => { + render(); + + await waitFor(() => expect(screen.queryByText('Loading, please wait...')).not.toBeInTheDocument()); + + // set order type to perm change and verify the "has dependents" state + await userEvent.selectOptions(screen.getByLabelText(/Orders type/), 'PERMANENT_CHANGE_OF_STATION'); + + const hasDependentsYesPermChg = screen.getByLabelText('Yes'); + const hasDependentsNoPermChg = screen.getByLabelText('No'); + + await waitFor(() => { + expect(hasDependentsYesPermChg).not.toBeChecked(); + expect(hasDependentsYesPermChg).toBeEnabled(); + expect(hasDependentsNoPermChg).not.toBeChecked(); + expect(hasDependentsNoPermChg).toBeEnabled(); + }); + + // set order type to value that disables and defaults "has dependents" + await userEvent.selectOptions(screen.getByLabelText(/Orders type/), ORDERS_TYPE.STUDENT_TRAVEL); + + // set order type to value the re-enables "has dependents" + await userEvent.selectOptions(screen.getByLabelText(/Orders type/), 'LOCAL_MOVE'); + + const hasDependentsYesLocalMove = screen.getByLabelText('Yes'); + const hasDependentsNoLocalMove = screen.getByLabelText('No'); + + await waitFor(() => { + expect(hasDependentsYesLocalMove).not.toBeChecked(); + expect(hasDependentsYesLocalMove).toBeEnabled(); + expect(hasDependentsNoLocalMove).not.toBeChecked(); + expect(hasDependentsNoLocalMove).toBeEnabled(); + }); + }); + + it('has dependents becomes disabled and then re-enabled for order type early return', async () => { + render(); + + await waitFor(() => expect(screen.queryByText('Loading, please wait...')).not.toBeInTheDocument()); + + // set order type to perm change and verify the "has dependents" state + await userEvent.selectOptions(screen.getByLabelText(/Orders type/), 'PERMANENT_CHANGE_OF_STATION'); + + const hasDependentsYesPermChg = screen.getByLabelText('Yes'); + const hasDependentsNoPermChg = screen.getByLabelText('No'); + + await waitFor(() => { + expect(hasDependentsYesPermChg).not.toBeChecked(); + expect(hasDependentsYesPermChg).toBeEnabled(); + expect(hasDependentsNoPermChg).not.toBeChecked(); + expect(hasDependentsNoPermChg).toBeEnabled(); + }); + + // set order type to value that disables and defaults "has dependents" + await userEvent.selectOptions(screen.getByLabelText(/Orders type/), ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS); + + // set order type to value the re-enables "has dependents" + await userEvent.selectOptions(screen.getByLabelText(/Orders type/), 'LOCAL_MOVE'); + + const hasDependentsYesLocalMove = screen.getByLabelText('Yes'); + const hasDependentsNoLocalMove = screen.getByLabelText('No'); + + await waitFor(() => { + expect(hasDependentsYesLocalMove).not.toBeChecked(); + expect(hasDependentsYesLocalMove).toBeEnabled(); + expect(hasDependentsNoLocalMove).not.toBeChecked(); + expect(hasDependentsNoLocalMove).toBeEnabled(); + }); + }); + afterEach(jest.restoreAllMocks); }); diff --git a/src/components/Customer/OrdersInfoForm/OrdersInfoForm.jsx b/src/components/Customer/OrdersInfoForm/OrdersInfoForm.jsx index d10fa8792f5..e7745f405a4 100644 --- a/src/components/Customer/OrdersInfoForm/OrdersInfoForm.jsx +++ b/src/components/Customer/OrdersInfoForm/OrdersInfoForm.jsx @@ -11,7 +11,7 @@ import styles from './OrdersInfoForm.module.scss'; import MaskedTextField from 'components/form/fields/MaskedTextField/MaskedTextField'; import ToolTip from 'shared/ToolTip/ToolTip'; -import { ORDERS_PAY_GRADE_OPTIONS } from 'constants/orders'; +import { ORDERS_PAY_GRADE_OPTIONS, ORDERS_TYPE } from 'constants/orders'; import { DropdownInput, DatePickerInput, DutyLocationInput } from 'components/form/fields'; import Hint from 'components/Hint/index'; import { Form } from 'components/form/Form'; @@ -36,6 +36,10 @@ const OrdersInfoForm = ({ ordersTypeOptions, initialValues, onSubmit, onBack }) const [hasDependents, setHasDependents] = useState(false); const [isOconusMove, setIsOconusMove] = useState(false); const [enableUB, setEnableUB] = useState(false); + const [isHasDependentsDisabled, setHasDependentsDisabled] = useState(false); + const [prevOrderType, setPrevOrderType] = useState(''); + const [filteredOrderTypeOptions, setFilteredOrderTypeOptions] = useState(ordersTypeOptions); + const validationSchema = Yup.object().shape({ orders_type: Yup.mixed() .oneOf(ordersTypeOptions.map((i) => i.key)) @@ -106,6 +110,21 @@ const OrdersInfoForm = ({ ordersTypeOptions, initialValues, onSubmit, onBack }) } }, [currentDutyLocation, newDutyLocation, isOconusMove, hasDependents, enableUB]); + useEffect(() => { + const fetchData = async () => { + const alaskaEnabled = await isBooleanFlagEnabled(FEATURE_FLAG_KEYS.ENABLE_ALASKA); + + const updatedOptions = alaskaEnabled + ? ordersTypeOptions + : ordersTypeOptions.filter( + (e) => e.key !== ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS && e.key !== ORDERS_TYPE.STUDENT_TRAVEL, + ); + + setFilteredOrderTypeOptions(updatedOptions); + }; + fetchData(); + }, [ordersTypeOptions]); + return ( - {({ isValid, isSubmitting, handleSubmit, values, touched, setFieldValue }) => { + {({ isValid, isSubmitting, handleSubmit, handleChange, values, touched, setFieldValue }) => { const isRetirementOrSeparation = ['RETIREMENT', 'SEPARATION'].includes(values.orders_type); if (!values.origin_duty_location && touched.origin_duty_location) originMeta = 'Required'; @@ -127,18 +146,39 @@ const OrdersInfoForm = ({ ordersTypeOptions, initialValues, onSubmit, onBack }) const handleHasDependentsChange = (e) => { // Declare a duplicate local scope of the field value // for the form to prevent state race conditions - const fieldValueHasDependents = e.target.value === 'yes'; - setHasDependents(e.target.value === 'yes'); - setFieldValue('has_dependents', fieldValueHasDependents ? 'yes' : 'no'); - if (fieldValueHasDependents && isOconusMove && enableUB) { - setShowAccompaniedTourField(true); - setShowDependentAgeFields(true); + if (e.target.value === '') { + setFieldValue('has_dependents', ''); } else { - setShowAccompaniedTourField(false); - setShowDependentAgeFields(false); + const fieldValueHasDependents = e.target.value === 'yes'; + setHasDependents(e.target.value === 'yes'); + setFieldValue('has_dependents', fieldValueHasDependents ? 'yes' : 'no'); + if (fieldValueHasDependents && isOconusMove && enableUB) { + setShowAccompaniedTourField(true); + setShowDependentAgeFields(true); + } else { + setShowAccompaniedTourField(false); + setShowDependentAgeFields(false); + } } }; + const handleOrderTypeChange = (e) => { + const { value } = e.target; + if (value === ORDERS_TYPE.STUDENT_TRAVEL || value === ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS) { + setHasDependentsDisabled(true); + handleHasDependentsChange({ target: { value: 'yes' } }); + } else { + setHasDependentsDisabled(false); + if ( + prevOrderType === ORDERS_TYPE.STUDENT_TRAVEL || + prevOrderType === ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS + ) { + handleHasDependentsChange({ target: { value: '' } }); + } + } + setPrevOrderType(value); + }; + return (

Tell us about your move orders

@@ -147,9 +187,13 @@ const OrdersInfoForm = ({ ordersTypeOptions, initialValues, onSubmit, onBack }) { + handleChange(e); + handleOrderTypeChange(e); + }} /> - { handleHasDependentsChange(e); }} + disabled={isHasDependentsDisabled} /> { handleHasDependentsChange(e); }} + disabled={isHasDependentsDisabled} /> diff --git a/src/components/Customer/OrdersInfoForm/OrdersInfoForm.test.jsx b/src/components/Customer/OrdersInfoForm/OrdersInfoForm.test.jsx index c44d164fdd0..245329a2cc7 100644 --- a/src/components/Customer/OrdersInfoForm/OrdersInfoForm.test.jsx +++ b/src/components/Customer/OrdersInfoForm/OrdersInfoForm.test.jsx @@ -2,10 +2,12 @@ import React from 'react'; import { render, waitFor, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; +import { isBooleanFlagEnabled } from '../../../utils/featureFlags'; + import OrdersInfoForm from './OrdersInfoForm'; import { showCounselingOffices } from 'services/internalApi'; -import { ORDERS_TYPE } from 'constants/orders'; +import { ORDERS_TYPE, ORDERS_TYPE_OPTIONS } from 'constants/orders'; jest.setTimeout(60000); @@ -150,6 +152,10 @@ jest.mock('components/LocationSearchBox/api', () => ({ ), })); +jest.mock('../../../utils/featureFlags', () => ({ + isBooleanFlagEnabled: jest.fn(), +})); + const testProps = { onSubmit: jest.fn().mockImplementation(() => Promise.resolve()), initialValues: { @@ -168,6 +174,8 @@ const testProps = { { key: 'RETIREMENT', value: 'Retirement' }, { key: 'SEPARATION', value: 'Separation' }, { key: 'TEMPORARY_DUTY', value: 'Temporary Duty (TDY)' }, + { key: ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS, value: ORDERS_TYPE_OPTIONS.EARLY_RETURN_OF_DEPENDENTS }, + { key: ORDERS_TYPE.STUDENT_TRAVEL, value: ORDERS_TYPE_OPTIONS.STUDENT_TRAVEL }, ], }; @@ -191,6 +199,8 @@ describe('OrdersInfoForm component', () => { }); it('renders each option for orders type', async () => { + isBooleanFlagEnabled.mockImplementation(() => Promise.resolve(true)); + showCounselingOffices.mockImplementation(() => Promise.resolve({})); const { getByLabelText } = render(); @@ -211,6 +221,12 @@ describe('OrdersInfoForm component', () => { await userEvent.selectOptions(ordersTypeDropdown, ORDERS_TYPE.TEMPORARY_DUTY); expect(ordersTypeDropdown).toHaveValue(ORDERS_TYPE.TEMPORARY_DUTY); + + await userEvent.selectOptions(ordersTypeDropdown, ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS); + expect(ordersTypeDropdown).toHaveValue(ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS); + + await userEvent.selectOptions(ordersTypeDropdown, ORDERS_TYPE.STUDENT_TRAVEL); + expect(ordersTypeDropdown).toHaveValue(ORDERS_TYPE.STUDENT_TRAVEL); }); it('allows new and current duty location to be the same', async () => { @@ -280,6 +296,8 @@ describe('OrdersInfoForm component', () => { { key: 'RETIREMENT', value: 'Retirement' }, { key: 'SEPARATION', value: 'Separation' }, { key: 'TEMPORARY_DUTY', value: 'Temporary Duty (TDY)' }, + { key: ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS, value: ORDERS_TYPE_OPTIONS.EARLY_RETURN_OF_DEPENDENTS }, + { key: ORDERS_TYPE.STUDENT_TRAVEL, value: ORDERS_TYPE_OPTIONS.STUDENT_TRAVEL }, ], }; @@ -574,5 +592,101 @@ describe('OrdersInfoForm component', () => { }); }); + it('has dependents is yes and disabled when order type is student travel', async () => { + render(); + + await userEvent.selectOptions(screen.getByLabelText(/Orders type/), ORDERS_TYPE.STUDENT_TRAVEL); + + const hasDependentsYes = screen.getByLabelText('Yes'); + const hasDependentsNo = screen.getByLabelText('No'); + + await waitFor(() => { + expect(hasDependentsYes).toBeChecked(); + expect(hasDependentsYes).toBeDisabled(); + expect(hasDependentsNo).toBeDisabled(); + }); + }); + + it('has dependents is yes and disabled when order type is early return', async () => { + render(); + + await userEvent.selectOptions(screen.getByLabelText(/Orders type/), ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS); + + const hasDependentsYes = screen.getByLabelText('Yes'); + const hasDependentsNo = screen.getByLabelText('No'); + + await waitFor(() => { + expect(hasDependentsYes).toBeChecked(); + expect(hasDependentsYes).toBeDisabled(); + expect(hasDependentsNo).toBeDisabled(); + }); + }); + + it('has dependents becomes disabled and then re-enabled for order type student travel', async () => { + render(); + + // set order type to perm change and verify the "has dependents" state + await userEvent.selectOptions(screen.getByLabelText(/Orders type/), 'PERMANENT_CHANGE_OF_STATION'); + + const hasDependentsYesPermChg = screen.getByLabelText('Yes'); + const hasDependentsNoPermChg = screen.getByLabelText('No'); + + await waitFor(() => { + expect(hasDependentsYesPermChg).not.toBeChecked(); + expect(hasDependentsYesPermChg).toBeEnabled(); + expect(hasDependentsNoPermChg).not.toBeChecked(); + expect(hasDependentsNoPermChg).toBeEnabled(); + }); + + // set order type to value that disables and defaults "has dependents" + await userEvent.selectOptions(screen.getByLabelText(/Orders type/), ORDERS_TYPE.STUDENT_TRAVEL); + + // set order type to value the re-enables "has dependents" + await userEvent.selectOptions(screen.getByLabelText(/Orders type/), 'LOCAL_MOVE'); + + const hasDependentsYesLocalMove = screen.getByLabelText('Yes'); + const hasDependentsNoLocalMove = screen.getByLabelText('No'); + + await waitFor(() => { + expect(hasDependentsYesLocalMove).not.toBeChecked(); + expect(hasDependentsYesLocalMove).toBeEnabled(); + expect(hasDependentsNoLocalMove).not.toBeChecked(); + expect(hasDependentsNoLocalMove).toBeEnabled(); + }); + }); + + it('has dependents becomes disabled and then re-enabled for order type early return', async () => { + render(); + + // set order type to perm change and verify the "has dependents" state + await userEvent.selectOptions(screen.getByLabelText(/Orders type/), 'PERMANENT_CHANGE_OF_STATION'); + + const hasDependentsYesPermChg = screen.getByLabelText('Yes'); + const hasDependentsNoPermChg = screen.getByLabelText('No'); + + await waitFor(() => { + expect(hasDependentsYesPermChg).not.toBeChecked(); + expect(hasDependentsYesPermChg).toBeEnabled(); + expect(hasDependentsNoPermChg).not.toBeChecked(); + expect(hasDependentsNoPermChg).toBeEnabled(); + }); + + // set order type to value that disables and defaults "has dependents" + await userEvent.selectOptions(screen.getByLabelText(/Orders type/), ORDERS_TYPE.EARLY_RETURN_OF_DEPENDENTS); + + // set order type to value the re-enables "has dependents" + await userEvent.selectOptions(screen.getByLabelText(/Orders type/), 'LOCAL_MOVE'); + + const hasDependentsYesLocalMove = screen.getByLabelText('Yes'); + const hasDependentsNoLocalMove = screen.getByLabelText('No'); + + await waitFor(() => { + expect(hasDependentsYesLocalMove).not.toBeChecked(); + expect(hasDependentsYesLocalMove).toBeEnabled(); + expect(hasDependentsNoLocalMove).not.toBeChecked(); + expect(hasDependentsNoLocalMove).toBeEnabled(); + }); + }); + afterEach(jest.restoreAllMocks); }); diff --git a/src/constants/orders.js b/src/constants/orders.js index eda945f3158..eb74a318b38 100644 --- a/src/constants/orders.js +++ b/src/constants/orders.js @@ -4,6 +4,8 @@ export const ORDERS_TYPE = { RETIREMENT: 'RETIREMENT', SEPARATION: 'SEPARATION', TEMPORARY_DUTY: 'TEMPORARY_DUTY', + EARLY_RETURN_OF_DEPENDENTS: 'EARLY_RETURN_OF_DEPENDENTS', + STUDENT_TRAVEL: 'STUDENT_TRAVEL', }; export const SPECIAL_ORDERS_TYPES = { @@ -24,6 +26,8 @@ export const ORDERS_TYPE_OPTIONS = { WOUNDED_WARRIOR: 'Wounded Warrior', BLUEBARK: 'BLUEBARK', TEMPORARY_DUTY: 'Temporary Duty (TDY)', + EARLY_RETURN_OF_DEPENDENTS: 'Early Return of Dependents', + STUDENT_TRAVEL: 'Student Travel', }; export const ORDERS_TYPE_DETAILS = { diff --git a/src/pages/MyMove/EditOrders.jsx b/src/pages/MyMove/EditOrders.jsx index bcc78eb51ed..e3d2055f828 100644 --- a/src/pages/MyMove/EditOrders.jsx +++ b/src/pages/MyMove/EditOrders.jsx @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { useNavigate, useParams } from 'react-router-dom'; +import { isBooleanFlagEnabled } from 'utils/featureFlags'; import Alert from 'shared/Alert'; import { withContext } from 'shared/AppContext'; import scrollToTop from 'shared/scrollToTop'; @@ -24,6 +25,7 @@ import { import EditOrdersForm from 'components/Customer/EditOrdersForm/EditOrdersForm'; import { formatWeight, formatYesNoInputValue, formatYesNoAPIValue, dropdownInputOptions } from 'utils/formatters'; import { ORDERS_TYPE_OPTIONS } from 'constants/orders'; +import { FEATURE_FLAG_KEYS } from 'shared/constants'; import { formatDateForSwagger } from 'shared/dates'; import LoadingPlaceholder from 'shared/LoadingPlaceholder'; @@ -40,6 +42,7 @@ const EditOrders = ({ const navigate = useNavigate(); const { moveId, orderId } = useParams(); const [serverError, setServerError] = useState(''); + const [orderTypes, setOrderTypes] = useState(ORDERS_TYPE_OPTIONS); const currentOrder = orders.find((order) => order.moves[0] === moveId); const { entitlement: allowances } = currentOrder; @@ -57,6 +60,19 @@ const EditOrders = ({ isMoveApproved = checkIfMoveStatusIsApproved(move.status); } + useEffect(() => { + const checkAlaskaFeatureFlag = async () => { + const isAlaskaEnabled = await isBooleanFlagEnabled(FEATURE_FLAG_KEYS.ENABLE_ALASKA); + if (!isAlaskaEnabled) { + const options = orderTypes; + delete orderTypes.EARLY_RETURN_OF_DEPENDENTS; + delete orderTypes.STUDENT_TRAVEL; + setOrderTypes(options); + } + }; + checkAlaskaFeatureFlag(); + }, [orderTypes]); + useEffect(() => { const fetchData = async () => { getOrders(orderId).then((response) => { @@ -89,7 +105,7 @@ const EditOrders = ({ const showAllOrdersTypes = context.flags?.allOrdersTypes; const allowedOrdersTypes = showAllOrdersTypes - ? ORDERS_TYPE_OPTIONS + ? orderTypes : { PERMANENT_CHANGE_OF_STATION: ORDERS_TYPE_OPTIONS.PERMANENT_CHANGE_OF_STATION }; const ordersTypeOptions = dropdownInputOptions(allowedOrdersTypes); diff --git a/src/pages/MyMove/PPM/Closeout/Feedback/Feedback.test.jsx b/src/pages/MyMove/PPM/Closeout/Feedback/Feedback.test.jsx index 9fad970b1c9..3ac8daea4d8 100644 --- a/src/pages/MyMove/PPM/Closeout/Feedback/Feedback.test.jsx +++ b/src/pages/MyMove/PPM/Closeout/Feedback/Feedback.test.jsx @@ -1,10 +1,17 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import { v4 } from 'uuid'; +import { MemoryRouter } from 'react-router-dom'; +import { Provider } from 'react-redux'; +import configureStore from 'redux-mock-store'; +import * as reactRedux from 'react-redux'; +import * as reactRouterDom from 'react-router-dom'; import Feedback, { GetTripWeight, FormatRow } from './Feedback'; +import * as formatters from 'utils/formatters'; import { MockProviders } from 'testUtils'; +import * as selectors from 'store/entities/selectors'; import { selectMTOShipmentById } from 'store/entities/selectors'; import { customerRoutes } from 'constants/routes'; @@ -19,12 +26,6 @@ const mockRoutingConfig = { }, }; -const mockNavigate = jest.fn(); -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useNavigate: () => mockNavigate, -})); - const mockMTOShipment = { ppmShipment: { actualDestinationPostalCode: '20889', @@ -49,6 +50,27 @@ const mockMTOShipment = { }, }; +const mockMTOShipmentWithAdvance = { + ppmShipment: { + actualDestinationPostalCode: '20889', + actualMoveDate: '2024-05-08', + actualPickupPostalCode: '59402', + hasReceivedAdvance: true, + advanceAmountReceived: 100000, + movingExpenses: [{ id: 'exp1', amount: 5000 }], + proGearWeightTickets: [{ id: 'pg1', weight: 75 }], + w2Address: { + city: 'Missoula', + county: 'MISSOULA', + id: '44fdfd2c-215c-48a0-8d41-065dbe38885b', + postalCode: '59801', + state: 'MT', + streetAddress1: '422 Dearborn Ave', + }, + weightTickets: [{ id: 'wt1', fullWeight: 3000, emptyWeight: 1500 }], + }, +}; + jest.mock('store/entities/selectors', () => ({ ...jest.requireActual('store/entities/selectors'), selectMTOShipmentById: jest.fn(() => mockMTOShipment), @@ -58,7 +80,9 @@ beforeEach(() => { jest.clearAllMocks(); }); -const renderFeedbackPage = () => { +const renderFeedbackPage = (mockData) => { + // Set the mock selector to return the specified mock data + selectMTOShipmentById.mockReturnValue(mockData); return render( @@ -67,8 +91,14 @@ const renderFeedbackPage = () => { }; describe('Feedback page', () => { + const mockNavigate = jest.fn(); + jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useNavigate: () => mockNavigate, + })); + it('displays PPM details', () => { - renderFeedbackPage(); + renderFeedbackPage(mockMTOShipment); expect(selectMTOShipmentById).toHaveBeenCalledWith(expect.anything(), mockMTOShipmentId); expect(screen.getByText('About Your PPM')).toBeInTheDocument(); @@ -80,24 +110,49 @@ describe('Feedback page', () => { }); it('formats and displays trip weight', () => { - renderFeedbackPage(); + renderFeedbackPage(mockMTOShipment); expect(screen.getByText('Trip weight:')).toBeInTheDocument(); expect(screen.getByText('3,845 lbs')).toBeInTheDocument(); }); it('does not display pro-gear if no pro-gear documents are present', () => { - renderFeedbackPage(); + renderFeedbackPage(mockMTOShipment); expect(screen.queryByTestId('pro-gear-items')).not.toBeInTheDocument(); }); it('does not display expenses if no expense documents are present', () => { - renderFeedbackPage(); + renderFeedbackPage(mockMTOShipment); expect(screen.queryByTestId('expenses-items')).not.toBeInTheDocument(); }); + it('displays weight moved section if weight tickets are present', () => { + renderFeedbackPage(mockMTOShipmentWithAdvance); + + const weightMovedHeading = screen.getByText('Weight Moved'); + const weightMovedValue = weightMovedHeading.closest('.headingContent').querySelector('span'); + + expect(weightMovedValue).toHaveTextContent('1,500 lbs'); + }); + + it('displays pro-gear section if pro-gear weight tickets are present', () => { + renderFeedbackPage(mockMTOShipmentWithAdvance); + + expect(screen.getByTestId('pro-gear-items')).toBeInTheDocument(); + expect(screen.getByText('Pro-gear')).toBeInTheDocument(); + expect(screen.getByText('75 lbs')).toBeInTheDocument(); + }); + + it('displays expenses section if moving expenses are present', () => { + renderFeedbackPage(mockMTOShipmentWithAdvance); + + expect(screen.getByTestId('expenses-items')).toBeInTheDocument(); + expect(screen.getByText('Expenses')).toBeInTheDocument(); + expect(screen.getByText('- $50.00')).toBeInTheDocument(); + }); + it('returns correct trip weight', () => { const doc = { fullWeight: 5844, emptyWeight: 1999 }; expect(GetTripWeight(doc)).toBe(3845); @@ -109,3 +164,100 @@ describe('Feedback page', () => { expect(formattedRow.value).toBe('$1000'); }); }); + +describe('Additional code coverage tests', () => { + const mockStore = configureStore([]); + const store = mockStore({}); + + const mockNavigate = jest.fn(); + + beforeEach(() => { + jest.spyOn(reactRouterDom, 'useNavigate').mockReturnValue(mockNavigate); + jest.spyOn(reactRouterDom, 'useParams').mockReturnValue({ mtoShipmentId: '1234' }); + jest.spyOn(selectors, 'selectMTOShipmentById').mockReturnValue({ + /* Mock shipment data */ + }); + jest.spyOn(formatters, 'formatCentsTruncateWhole').mockReturnValue('Mocked Amount'); + jest.spyOn(formatters, 'formatCustomerDate').mockReturnValue('Mocked Date'); + jest.spyOn(formatters, 'formatWeight').mockReturnValue('Mocked Weight'); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('renders LoadingPlaceholder when no shipment data is available (line 47)', () => { + jest.spyOn(reactRedux, 'useSelector').mockReturnValue(null); // No shipment data + + render( + + + + + , + ); + + expect(screen.getByText(/Loading/)).toBeInTheDocument(); + }); + + it('calculates trip weight correctly (lines 64-65)', () => { + const ppmShipment = { + weightTickets: [{ weight: 2000 }], + proGearWeightTickets: [], // Initialize as empty array + movingExpenses: [], // Initialize as empty array + }; + jest.spyOn(reactRedux, 'useSelector').mockReturnValue({ ppmShipment }); + + render( + + + + + , + ); + + expect(screen.getByText(/Weight Moved/)).toBeInTheDocument(); + }); + + it('formats single document for feedback item (line 92)', () => { + const ppmShipment = { + weightTickets: [{ weight: 1000, status: 'REJECTED', reason: 'Incorrect weight' }], + proGearWeightTickets: [], // Initialize as empty array + movingExpenses: [], // Initialize as empty array + }; + jest.spyOn(reactRedux, 'useSelector').mockReturnValue({ ppmShipment }); + + render( + + + + + , + ); + + expect(screen.getByText(/Incorrect weight/)).toBeInTheDocument(); + }); + + it('displays pro-gear items when available (line 108)', () => { + const ppmShipment = { + weightTickets: [{ weight: 2000 }], + proGearWeightTickets: [{ weight: 500 }], + movingExpenses: [], // Initialize as empty array + }; + jest.spyOn(reactRedux, 'useSelector').mockReturnValue({ ppmShipment }); + + render( + + + + + , + ); + + expect(screen.getByTestId('pro-gear-items')).toBeInTheDocument(); + }); + + afterAll(() => { + jest.restoreAllMocks(); + }); +}); diff --git a/swagger-def/definitions/OrdersType.yaml b/swagger-def/definitions/OrdersType.yaml index 2570569e773..95e2d032a25 100644 --- a/swagger-def/definitions/OrdersType.yaml +++ b/swagger-def/definitions/OrdersType.yaml @@ -9,6 +9,8 @@ enum: - BLUEBARK - SAFETY - TEMPORARY_DUTY + - EARLY_RETURN_OF_DEPENDENTS + - STUDENT_TRAVEL x-display-value: PERMANENT_CHANGE_OF_STATION: Permanent Change Of Station LOCAL_MOVE: Local Move @@ -18,3 +20,5 @@ x-display-value: BLUEBARK: BLUEBARK SAFETY: Safety TEMPORARY_DUTY: Temporary Duty (TDY) + EARLY_RETURN_OF_DEPENDENTS: Early Return of Dependents + STUDENT_TRAVEL: Student Travel diff --git a/swagger-def/support.yaml b/swagger-def/support.yaml index 9875d69bcaa..e3bf2dd4528 100644 --- a/swagger-def/support.yaml +++ b/swagger-def/support.yaml @@ -1630,6 +1630,8 @@ definitions: - WOUNDED_WARRIOR - BLUEBARK - TEMPORARY_DUTY + - EARLY_RETURN_OF_DEPENDENTS + - STUDENT_TRAVEL x-display-value: PERMANENT_CHANGE_OF_STATION: Permanent Change Of Station (PCS) LOCAL_MOVE: Local Move @@ -1640,6 +1642,8 @@ definitions: WOUNDED_WARRIOR: Wounded Warrior BLUEBARK: BLUEBARK TEMPORARY_DUTY: Temporary Duty (TDY) + EARLY_RETURN_OF_DEPENDENTS: Early Return of Dependents + STUDENT_TRAVEL: Student Travel OrdersTypeDetail: type: string title: Orders type detail diff --git a/swagger/ghc.yaml b/swagger/ghc.yaml index 7d446cc679e..fcc67f9faf2 100644 --- a/swagger/ghc.yaml +++ b/swagger/ghc.yaml @@ -8359,6 +8359,8 @@ definitions: - BLUEBARK - SAFETY - TEMPORARY_DUTY + - EARLY_RETURN_OF_DEPENDENTS + - STUDENT_TRAVEL x-display-value: PERMANENT_CHANGE_OF_STATION: Permanent Change Of Station LOCAL_MOVE: Local Move @@ -8368,6 +8370,8 @@ definitions: BLUEBARK: BLUEBARK SAFETY: Safety TEMPORARY_DUTY: Temporary Duty (TDY) + EARLY_RETURN_OF_DEPENDENTS: Early Return of Dependents + STUDENT_TRAVEL: Student Travel Upload: description: An uploaded file. type: object diff --git a/swagger/internal.yaml b/swagger/internal.yaml index ec191b1e1fc..67d1ad4046a 100644 --- a/swagger/internal.yaml +++ b/swagger/internal.yaml @@ -2579,6 +2579,8 @@ definitions: - BLUEBARK - SAFETY - TEMPORARY_DUTY + - EARLY_RETURN_OF_DEPENDENTS + - STUDENT_TRAVEL x-display-value: PERMANENT_CHANGE_OF_STATION: Permanent Change Of Station LOCAL_MOVE: Local Move @@ -2588,6 +2590,8 @@ definitions: BLUEBARK: BLUEBARK SAFETY: Safety TEMPORARY_DUTY: Temporary Duty (TDY) + EARLY_RETURN_OF_DEPENDENTS: Early Return of Dependents + STUDENT_TRAVEL: Student Travel Address: description: A postal address type: object diff --git a/swagger/prime.yaml b/swagger/prime.yaml index fcf8a97fb05..a4e3558f121 100644 --- a/swagger/prime.yaml +++ b/swagger/prime.yaml @@ -3016,6 +3016,8 @@ definitions: - BLUEBARK - SAFETY - TEMPORARY_DUTY + - EARLY_RETURN_OF_DEPENDENTS + - STUDENT_TRAVEL x-display-value: PERMANENT_CHANGE_OF_STATION: Permanent Change Of Station LOCAL_MOVE: Local Move @@ -3025,6 +3027,8 @@ definitions: BLUEBARK: BLUEBARK SAFETY: Safety TEMPORARY_DUTY: Temporary Duty (TDY) + EARLY_RETURN_OF_DEPENDENTS: Early Return of Dependents + STUDENT_TRAVEL: Student Travel Order: type: object required: diff --git a/swagger/prime_v2.yaml b/swagger/prime_v2.yaml index a19dcc1247b..d35eefa7d09 100644 --- a/swagger/prime_v2.yaml +++ b/swagger/prime_v2.yaml @@ -1697,6 +1697,8 @@ definitions: - BLUEBARK - SAFETY - TEMPORARY_DUTY + - EARLY_RETURN_OF_DEPENDENTS + - STUDENT_TRAVEL x-display-value: PERMANENT_CHANGE_OF_STATION: Permanent Change Of Station LOCAL_MOVE: Local Move @@ -1706,6 +1708,8 @@ definitions: BLUEBARK: BLUEBARK SAFETY: Safety TEMPORARY_DUTY: Temporary Duty (TDY) + EARLY_RETURN_OF_DEPENDENTS: Early Return of Dependents + STUDENT_TRAVEL: Student Travel Order: type: object required: diff --git a/swagger/prime_v3.yaml b/swagger/prime_v3.yaml index 392a56093aa..5f1aa01106b 100644 --- a/swagger/prime_v3.yaml +++ b/swagger/prime_v3.yaml @@ -1781,6 +1781,8 @@ definitions: - BLUEBARK - SAFETY - TEMPORARY_DUTY + - EARLY_RETURN_OF_DEPENDENTS + - STUDENT_TRAVEL x-display-value: PERMANENT_CHANGE_OF_STATION: Permanent Change Of Station LOCAL_MOVE: Local Move @@ -1790,6 +1792,8 @@ definitions: BLUEBARK: BLUEBARK SAFETY: Safety TEMPORARY_DUTY: Temporary Duty (TDY) + EARLY_RETURN_OF_DEPENDENTS: Early Return of Dependents + STUDENT_TRAVEL: Student Travel Order: type: object required: diff --git a/swagger/support.yaml b/swagger/support.yaml index 4dbe72985b0..8b0e87f4842 100644 --- a/swagger/support.yaml +++ b/swagger/support.yaml @@ -1757,6 +1757,8 @@ definitions: - WOUNDED_WARRIOR - BLUEBARK - TEMPORARY_DUTY + - EARLY_RETURN_OF_DEPENDENTS + - STUDENT_TRAVEL x-display-value: PERMANENT_CHANGE_OF_STATION: Permanent Change Of Station (PCS) LOCAL_MOVE: Local Move @@ -1767,6 +1769,8 @@ definitions: WOUNDED_WARRIOR: Wounded Warrior BLUEBARK: BLUEBARK TEMPORARY_DUTY: Temporary Duty (TDY) + EARLY_RETURN_OF_DEPENDENTS: Early Return of Dependents + STUDENT_TRAVEL: Student Travel OrdersTypeDetail: type: string title: Orders type detail