diff --git a/src/appReducer.js b/src/appReducer.js index 998ddde0f4b..c5ae663f6b4 100644 --- a/src/appReducer.js +++ b/src/appReducer.js @@ -11,7 +11,6 @@ import { swaggerReducerPublic, swaggerReducerInternal } from 'shared/Swagger/duc import { requestsReducer } from 'shared/Swagger/requestsReducer'; import { entitiesReducer } from 'shared/Entities/reducer'; import { officeFlashMessagesReducer } from 'scenes/Office/ducks'; -import officePpmReducer from 'scenes/Office/Ppm/ducks'; const authPersistConfig = { key: 'auth', @@ -35,7 +34,6 @@ export const appReducer = () => swaggerInternal: swaggerReducerInternal, flashMessages: officeFlashMessagesReducer, interceptor: interceptorReducer, - ppmIncentive: officePpmReducer, }); export default appReducer; diff --git a/src/sagas/entities.js b/src/sagas/entities.js index d3ae0f6ef95..6f6f86b383d 100644 --- a/src/sagas/entities.js +++ b/src/sagas/entities.js @@ -7,10 +7,6 @@ import { UPDATE_MTO_SHIPMENT, UPDATE_MTO_SHIPMENTS, UPDATE_ORDERS, - UPDATE_PPMS, - UPDATE_PPM, - UPDATE_PPM_ESTIMATE, - UPDATE_PPM_SIT_ESTIMATE, } from 'store/entities/actions'; import { normalizeResponse } from 'services/swaggerRequest'; import { addEntities, updateMTOShipmentsEntity, setOktaUser } from 'shared/Entities/actions'; @@ -60,30 +56,6 @@ export function* updateMTOShipments(action) { yield put(updateMTOShipmentsEntity(payload)); } -export function* updatePPMs(action) { - const { payload } = action; - const normalizedData = yield call(normalizeResponse, payload, 'personallyProcuredMoves'); - yield put(addEntities(normalizedData)); -} - -export function* updatePPM(action) { - const { payload } = action; - const normalizedData = yield call(normalizeResponse, payload, 'personallyProcuredMove'); - yield put(addEntities(normalizedData)); -} - -export function* updatePPMEstimate(action) { - const { payload } = action; - const normalizedData = yield call(normalizeResponse, payload, 'ppmEstimateRange'); - yield put(addEntities(normalizedData)); -} - -export function* updatePPMSitEstimate(action) { - const { payload } = action; - const normalizedData = yield call(normalizeResponse, payload, 'ppmSitEstimate'); - yield put(addEntities(normalizedData)); -} - export function* watchUpdateEntities() { yield all([ takeLatest(UPDATE_SERVICE_MEMBER, updateServiceMember), @@ -92,9 +64,5 @@ export function* watchUpdateEntities() { takeLatest(UPDATE_MOVE, updateMove), takeLatest(UPDATE_MTO_SHIPMENT, updateMTOShipment), takeLatest(UPDATE_MTO_SHIPMENTS, updateMTOShipments), - takeLatest(UPDATE_PPMS, updatePPMs), - takeLatest(UPDATE_PPM, updatePPM), - takeLatest(UPDATE_PPM_ESTIMATE, updatePPMEstimate), - takeLatest(UPDATE_PPM_SIT_ESTIMATE, updatePPMSitEstimate), ]); } diff --git a/src/sagas/entities.test.js b/src/sagas/entities.test.js index 857cd28470c..9525c46ab90 100644 --- a/src/sagas/entities.test.js +++ b/src/sagas/entities.test.js @@ -8,10 +8,6 @@ import { updateMTOShipment, updateMTOShipments, updateOrders, - updatePPMs, - updatePPM, - updatePPMEstimate, - updatePPMSitEstimate, } from './entities'; import { @@ -20,10 +16,6 @@ import { UPDATE_MOVE, UPDATE_MTO_SHIPMENT, UPDATE_ORDERS, - UPDATE_PPMS, - UPDATE_PPM, - UPDATE_PPM_ESTIMATE, - UPDATE_PPM_SIT_ESTIMATE, UPDATE_MTO_SHIPMENTS, } from 'store/entities/actions'; import { normalizeResponse } from 'services/swaggerRequest'; @@ -41,10 +33,6 @@ describe('watchUpdateEntities', () => { takeLatest(UPDATE_MOVE, updateMove), takeLatest(UPDATE_MTO_SHIPMENT, updateMTOShipment), takeLatest(UPDATE_MTO_SHIPMENTS, updateMTOShipments), - takeLatest(UPDATE_PPMS, updatePPMs), - takeLatest(UPDATE_PPM, updatePPM), - takeLatest(UPDATE_PPM_ESTIMATE, updatePPMEstimate), - takeLatest(UPDATE_PPM_SIT_ESTIMATE, updatePPMSitEstimate), ]), ); }); @@ -248,89 +236,3 @@ describe('updateOrders', () => { expect(generator.next().done).toEqual(true); }); }); - -describe('updatePPM', () => { - const testAction = { - payload: { - actual_move_date: '2020-12-18', - advance_worksheet: { - id: '00000000-0000-0000-0000-000000000000', - service_member_id: '00000000-0000-0000-0000-000000000000', - uploads: [], - }, - approve_date: '2020-12-21T22:45:52.000Z', - created_at: '2020-12-21T22:43:48.278Z', - destination_postal_code: '99619', - has_additional_postal_code: false, - has_requested_advance: false, - has_sit: false, - id: 'd9488eac-eef8-430e-8c4b-05884c3cc6fa', - move_id: '2b8198ca-e70a-40b7-822e-be5527bf0606', - original_move_date: '2020-12-19', - pickup_postal_code: '10002', - status: 'PAYMENT_REQUESTED', - submit_date: '2020-12-21T22:45:12.100Z', - updated_at: '2020-12-21T22:46:50.805Z', - }, - }; - - const normalizedPPM = normalizeResponse(testAction.payload, 'personallyProcuredMove'); - - const generator = updatePPM(testAction); - - it('normalizes the payload', () => { - expect(generator.next().value).toEqual(call(normalizeResponse, testAction.payload, 'personallyProcuredMove')); - }); - - it('stores the normalized data in entities', () => { - expect(generator.next(normalizedPPM).value).toEqual(put(addEntities(normalizedPPM))); - }); - - it('is done', () => { - expect(generator.next().done).toEqual(true); - }); -}); - -describe('updatePPMs', () => { - const testAction = { - payload: [ - { - actual_move_date: '2020-12-18', - advance_worksheet: { - id: '00000000-0000-0000-0000-000000000000', - service_member_id: '00000000-0000-0000-0000-000000000000', - uploads: [], - }, - approve_date: '2020-12-21T22:45:52.000Z', - created_at: '2020-12-21T22:43:48.278Z', - destination_postal_code: '99619', - has_additional_postal_code: false, - has_requested_advance: false, - has_sit: false, - id: 'd9488eac-eef8-430e-8c4b-05884c3cc6fa', - move_id: '2b8198ca-e70a-40b7-822e-be5527bf0606', - original_move_date: '2020-12-19', - pickup_postal_code: '10002', - status: 'PAYMENT_REQUESTED', - submit_date: '2020-12-21T22:45:12.100Z', - updated_at: '2020-12-21T22:46:50.805Z', - }, - ], - }; - - const normalizedPPM = normalizeResponse(testAction.payload, 'personallyProcuredMoves'); - - const generator = updatePPMs(testAction); - - it('normalizes the payload', () => { - expect(generator.next().value).toEqual(call(normalizeResponse, testAction.payload, 'personallyProcuredMoves')); - }); - - it('stores the normalized data in entities', () => { - expect(generator.next(normalizedPPM).value).toEqual(put(addEntities(normalizedPPM))); - }); - - it('is done', () => { - expect(generator.next().done).toEqual(true); - }); -}); diff --git a/src/scenes/Moves/Ppm/AllowableExpenses.jsx b/src/scenes/Moves/Ppm/AllowableExpenses.jsx deleted file mode 100644 index 1be1662e5a6..00000000000 --- a/src/scenes/Moves/Ppm/AllowableExpenses.jsx +++ /dev/null @@ -1,139 +0,0 @@ -import React from 'react'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { useNavigate } from 'react-router-dom'; - -function AllowableExpenses(props) { - const navigate = useNavigate(); - function goBack() { - navigate(-1); - } - - return ( -
-
-
-
- - {'<'} Back - -
-

Storage & Moving Expenses

-

- Storage expenses are a special expense that is reimbursable for up to 90 - days. You can be directly repaid for those expenses. -

-

- The IRS considers the rest of your PPM payment as taxable income. You’ll receive a separate W-2 for any PPM - payment. -

-

- Moving-related expenses can be claimed in order to reduce the taxable - amount of your payment. Your{' '} - - local finance office - {' '} - or a tax professional can help you identify qualifying expenses. You can also consult{' '} - - IRS Publication 521 - {' '} - for authoritative information. -

-

- Save your receipts. It’s better to have receipts you don’t need than to need receipts you - don’t have. -

-
-
- - Some commonly claimed moving expenses: -
-
    -
  • Consumable packing materials
  • -
  • Contracted expenses
  • -
  • Oil
  • -
  • Rental equipment
  • -
  • Tolls
  • -
  • Weighing fees
  • -
  • Gas, exceeding travel allowance (see below)
  • -
-
-
- - Some common expenses that are not claimable or reimbursable: -
-
    -
  • Animal costs (kennels, transportation)
  • -
  • Extra drivers
  • -
  • Hitch fees and tow bars
  • -
  • Locks
  • -
  • Meals and lodging
  • -
  • Moving insurance
  • -
  • Oil change and routine maintenance
  • -
  • Purchased auto transporters and dollies
  • -
  • Sales tax
  • -
  • Tire chains
  • -
  • Gas, under travel allowance (see details following)
  • -
-
-
- Gas and fuel expenses -

Gas and fuel expenses are not reimbursable.

-

If you rented a vehicle to perform your move, you can claim gas expenses for tax purposes.

-

- You can not claim expenses for fuel for your own vehicles. You will be reimbursed for that fuel via DTS - when you claim your mileage. The IRS does not allow you to claim an expense twice, and may audit you if - you do so. -

-

- There is one rare exception: If your fuel expenses exceed the amount paid for mileage and per diem fees on - your travel pay. You may claim the portion of your fuel expenses for your own vehicles that exceeds that - amount. -

-
-
- When are receipts required? -

You must have receipts for contracted expenses, storage facilities, and any expense over $75.

-

- You will need receipts for expenses under $75 if multiple expenses in that same category add up to more - than $75. -

-

- Again, it’s better to have receipts you don’t need than to be missing receipts that you do need. We - recommend saving all your moving receipts. -

-

- If you are missing a receipt, you can go online and print a new copy of your receipt (if you can). - Otherwise, write and sign a statement that explains why the receipt is missing. Contact your{' '} - - local finance office - {' '} - for assistance. -

-
-
- -
-
-
-
- ); -} - -export default AllowableExpenses; diff --git a/src/scenes/Moves/Ppm/CustomerAgreementLegalese.js b/src/scenes/Moves/Ppm/CustomerAgreementLegalese.js deleted file mode 100644 index 246f28c6ca6..00000000000 --- a/src/scenes/Moves/Ppm/CustomerAgreementLegalese.js +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react'; -import { useNavigate } from 'react-router-dom'; - -function CustomerAgreementLegalese(props) { - const navigate = useNavigate(); - function goBack() { - navigate(-1); - } - - return ( -
-
-
-
- - {'<'} Back - -
-

Customer Agreement

-

- Before submitting your payment request, please carefully read the following: -

-
-

LEGAL AGREEMENT / PRIVACY ACT

-

Financial Liability:

- If this shipment(s) incurs costs above the allowance I am entitled to, I will pay the difference to the - government, or consent to the collection from my pay as necessary to cover all excess costs associated by - this shipment(s). -

Advance Obligation:

-

- I understand that the maximum advance allowed is based on the estimated weight and scheduled departure - date of my shipment(s). In the event, less weight is moved or my move occurs on a different scheduled - departure date, I may have to remit the difference with the balance of my incentive disbursement and/or - from the collection of my pay as may be necessary. -

-

- I understand that the maximum advance allowed is based on the estimated weight and scheduled departure - date of my shipment(s). In the event, less weight is moved or my move occurs on a different scheduled - departure date, I may have to remit the difference with the balance of my incentive disbursement and/or - from the collection of my pay as may be necessary. If I receive an advance for my PPM shipment, I agree to - furnish weight tickets within 45 days of final delivery to my destination. I understand that failure to - furnish weight tickets within this timeframe may lead to the collection of my pay as necessary to cover - the cost of the advance. -

-
-
- -
-
-
-
- ); -} - -export default CustomerAgreementLegalese; diff --git a/src/scenes/Moves/Ppm/DateAndLocation.css b/src/scenes/Moves/Ppm/DateAndLocation.css deleted file mode 100644 index 0971e8af619..00000000000 --- a/src/scenes/Moves/Ppm/DateAndLocation.css +++ /dev/null @@ -1,24 +0,0 @@ -label.inline_radio { - margin-top: 1px; - margin-bottom: 1px; -} - -h2 { - margin-top: 1em; -} - -.grey { - color: rgb(100, 100, 100); -} - -.days-in-storage > input { - max-width: 100px; -} - -.storage-estimate { - margin-top: 3rem; - background-color: #f4f2f2; - border: 1px solid #cccccc; - padding: 1em; - max-width: 700px; -} diff --git a/src/scenes/Moves/Ppm/Expenses.css b/src/scenes/Moves/Ppm/Expenses.css deleted file mode 100644 index 1235cb3d05b..00000000000 --- a/src/scenes/Moves/Ppm/Expenses.css +++ /dev/null @@ -1,57 +0,0 @@ -.expenses-container { - padding-top: 2em; -} - -.expenses-header { - margin-bottom: 0.5em; -} - -.expenses-container > p { - margin-bottom: 0; - margin-top: 0; -} - -.expenses-container .dashed-divider { - margin-top: 0; -} - -.expenses-container .expenses-uploader { - padding-top: 2em; -} - -.expenses-container a:visited { - color: #0071bc; -} - -.expenses-list { - margin-bottom: 1em; -} - -.has-expenses-radio-group { - margin-top: 1em; - margin-bottom: 3.5em; -} - -.payment-method-radio-group-wrapper { - margin-bottom: 0; - line-height: 1.8em; - margin-top: 1.8em; -} - -.expenses-group-header { - margin: 1.8em 0 -0.5em 0; -} - -.expense-form-element > label { - margin: 1.5em 0 0 0; -} - -.expenses-form-group { - /*border: red solid;*/ - margin: 0 2em 0 0; -} - -.expenses-form-group > label { - margin: 1em 0 0 0; -} - diff --git a/src/scenes/Moves/Ppm/ExpensesLanding.jsx b/src/scenes/Moves/Ppm/ExpensesLanding.jsx deleted file mode 100644 index e37f9265bb9..00000000000 --- a/src/scenes/Moves/Ppm/ExpensesLanding.jsx +++ /dev/null @@ -1,124 +0,0 @@ -import React, { Component } from 'react'; -import { Link } from 'react-router-dom'; - -import WizardHeader from '../WizardHeader'; - -import PPMPaymentRequestActionBtns from './PPMPaymentRequestActionBtns'; - -import { ProgressTimeline, ProgressTimelineStep } from 'shared/ProgressTimeline'; -import RadioButton from 'shared/RadioButton'; - -import './Expenses.css'; -import { connect } from 'react-redux'; - -import DocumentsUploaded from './PaymentReview/DocumentsUploaded'; - -import withRouter from 'utils/routing'; - -const reviewPagePath = '/ppm-payment-review'; -const nextPagePath = '/ppm-expenses'; - -class ExpensesLanding extends Component { - state = { - hasExpenses: '', - }; - - handleRadioChange = (event) => { - this.setState({ - [event.target.name]: event.target.value, - }); - }; - - saveAndAddHandler = () => { - const { - moveId, - router: { navigate }, - } = this.props; - const { hasExpenses } = this.state; - if (hasExpenses === 'No') { - return navigate(`/moves/${moveId}${reviewPagePath}`); - } - return navigate(`/moves/${moveId}${nextPagePath}`); - }; - - render() { - const { hasExpenses } = this.state; - const { - router: { navigate }, - moveId, - } = this.props; - return ( -
- - - - - - } - /> -
-
- -
-
- -
-
-

Do you have any storage or moving expenses?

-
    -
  • - Storage expenses are reimbursable. -
  • -
  • - Claimable moving expenses (such as weighing fees, rental equipment, or tolls){' '} - reduce taxes on your payment. -
  • -
- - More about expenses - -
- - -
- {}} - nextBtnLabel="Continue" - saveAndAddHandler={this.saveAndAddHandler} - finishLaterHandler={() => navigate('/')} - submitButtonsAreDisabled={!hasExpenses} - /> -
-
-
- ); - } -} - -function mapStateToProps(state, { router: { params } }) { - const { moveId } = params; - return { - moveId, - }; -} - -export default withRouter(connect(mapStateToProps)(ExpensesLanding)); diff --git a/src/scenes/Moves/Ppm/ExpensesUpload.jsx b/src/scenes/Moves/Ppm/ExpensesUpload.jsx deleted file mode 100644 index e24c16a3315..00000000000 --- a/src/scenes/Moves/Ppm/ExpensesUpload.jsx +++ /dev/null @@ -1,354 +0,0 @@ -import React, { Component } from 'react'; -import { get, isEmpty, map } from 'lodash'; -import { Link } from 'react-router-dom'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { getFormValues, reduxForm } from 'redux-form'; -import { connect } from 'react-redux'; - -import WizardHeader from '../WizardHeader'; - -import PPMPaymentRequestActionBtns from './PPMPaymentRequestActionBtns'; -import DocumentsUploaded from './PaymentReview/DocumentsUploaded'; - -import { ProgressTimeline, ProgressTimelineStep } from 'shared/ProgressTimeline'; -import { convertDollarsToCents } from 'shared/utils'; -import RadioButton from 'shared/RadioButton'; -import { SwaggerField } from 'shared/JsonSchemaForm/JsonSchemaField'; -import Uploader from 'shared/Uploader'; -import Checkbox from 'shared/Checkbox'; -import { - createMovingExpenseDocument, - selectPPMCloseoutDocumentsForMove, -} from 'shared/Entities/modules/movingExpenseDocuments'; -import Alert from 'shared/Alert'; -import { getMoveDocumentsForMove } from 'shared/Entities/modules/moveDocuments'; -import { withContext } from 'shared/AppContext'; -import { documentSizeLimitMsg } from 'shared/constants'; -import { selectCurrentPPM } from 'store/entities/selectors'; -import withRouter from 'utils/routing'; - -const nextPagePath = '/ppm-payment-review'; -const nextBtnLabels = { - SaveAndAddAnother: 'Save & Add Another', - SaveAndContinue: 'Save & Continue', -}; - -const paymentMethods = { - Other: 'OTHER', - GTCC: 'GTCC', -}; - -const uploadReceipt = - 'Drag & drop or click to upload receipt'; - -class ExpensesUpload extends Component { - state = { ...this.initialState }; - - get initialState() { - return { - paymentMethod: paymentMethods.GTCC, - uploaderIsIdle: true, - missingReceipt: false, - expenseType: '', - haveMoreExpenses: 'No', - moveDocumentCreateError: false, - }; - } - - componentDidMount() { - const { moveId } = this.props; - this.props.getMoveDocumentsForMove(moveId); - } - - handleRadioChange = (event) => { - this.setState({ - [event.target.name]: event.target.value, - }); - }; - - skipHandler = () => { - const { - moveId, - router: { navigate }, - } = this.props; - navigate(`/moves/${moveId}${nextPagePath}`); - }; - - isStorageExpense = (formValues) => { - return !isEmpty(formValues) && formValues.moving_expense_type === 'STORAGE'; - }; - - saveAndAddHandler = (formValues) => { - const { - moveId, - currentPpm, - router: { navigate }, - } = this.props; - - const { paymentMethod, missingReceipt, haveMoreExpenses } = this.state; - const { - storage_start_date, - storage_end_date, - moving_expense_type: movingExpenseType, - requested_amount_cents: requestedAmountCents, - } = formValues; - - const files = this.uploader.getFiles(); - const uploadIds = map(files, 'id'); - const personallyProcuredMoveId = currentPpm ? currentPpm.id : null; - const title = this.isStorageExpense(formValues) ? 'Storage Expense' : formValues.title; - return this.props - .createMovingExpenseDocument({ - moveId, - personallyProcuredMoveId, - uploadIds, - title, - movingExpenseType, - moveDocumentType: 'EXPENSE', - requestedAmountCents: convertDollarsToCents(requestedAmountCents), - paymentMethod, - notes: '', - missingReceipt, - storage_start_date, - storage_end_date, - }) - .then(() => { - this.cleanup(); - if (haveMoreExpenses === 'No') { - navigate(`/moves/${moveId}${nextPagePath}`); - } - }) - .catch((e) => { - this.setState({ moveDocumentCreateError: true }); - }); - }; - - cleanup = () => { - const { reset } = this.props; - this.uploader.clearFiles(); - reset(); - this.setState({ ...this.initialState }); - }; - - onAddFile = () => { - this.setState({ - uploaderIsIdle: false, - }); - }; - - onChange = (newUploads, uploaderIsIdle) => { - this.setState({ - uploaderIsIdle, - }); - }; - - handleCheckboxChange = (event) => { - this.setState({ - [event.target.name]: event.target.checked, - }); - }; - - handleHowDidYouPayForThis = () => { - alert('Cash, personal credit card, check — any payment method that’s not your GTCC.'); - }; - - isInvalidUploaderState = () => { - const { missingReceipt } = this.state; - const receiptUploaded = this.uploader && !this.uploader.isEmpty(); - return missingReceipt === receiptUploaded; - }; - - render() { - const { missingReceipt, paymentMethod, haveMoreExpenses, moveDocumentCreateError } = this.state; - const { moveDocSchema, formValues, isPublic, handleSubmit, submitting, expenses, expenseSchema, invalid, moveId } = - this.props; - const nextBtnLabel = haveMoreExpenses === 'Yes' ? nextBtnLabels.SaveAndAddAnother : nextBtnLabels.SaveAndContinue; - const hasMovingExpenseType = !isEmpty(formValues) && formValues.moving_expense_type !== ''; - const isStorageExpense = this.isStorageExpense(formValues); - const expenseNumber = expenses.length + 1; - return ( -
- - - - - - } - /> -
- -
- -
-

Expense {expenseNumber}

-

- Upload expenses one at a time.{' '} - - - -

-
- {moveDocumentCreateError && ( -
-
- - Something went wrong contacting the server. - -
-
- )} - - {hasMovingExpenseType && ( - <> - {isStorageExpense ? ( -
-

Dates

- - -
- ) : ( - - )} - -
-

{documentSizeLimitMsg}

- (this.uploader = ref)} - onChange={this.onChange} - onAddFile={this.onAddFile} - /> -
- - {isStorageExpense && missingReceipt && ( - - - If you can, go online and print a new copy of your receipt, then upload it.
- Otherwise, write and sign a statement that explains why this receipt is missing, then upload it. - Finance will approve or reject this expense based on your information. -
-
- )} -
-

How did you pay for this?

- - - -
-
-
-

Do you have more expenses to upload?

- - -
- - )} - = 1} - saveAndAddHandler={handleSubmit(this.saveAndAddHandler)} - /> - -
-
- ); - } -} - -const formName = 'expense_document_upload'; -ExpensesUpload = reduxForm({ form: formName })(ExpensesUpload); - -function mapStateToProps(state, props) { - const { - router: { params: moveId }, - } = props; - - return { - moveId, - formValues: getFormValues(formName)(state), - moveDocSchema: get(state, 'swaggerInternal.spec.definitions.MoveDocumentPayload', {}), - expenseSchema: get(state, 'swaggerInternal.spec.definitions.CreateMovingExpenseDocumentPayload', {}), - currentPpm: selectCurrentPPM(state) || {}, - expenses: selectPPMCloseoutDocumentsForMove(state, moveId, ['EXPENSE']), - }; -} - -const mapDispatchToProps = { - // TODO we can possibly remove selectPPMCloseoutDocumentsForMove and - // getMoveDocumentsForMove once the document reviewer component is added - // as it may be possible to get the number of expenses from that. - selectPPMCloseoutDocumentsForMove, - getMoveDocumentsForMove, - createMovingExpenseDocument, -}; - -export default withContext(withRouter(connect(mapStateToProps, mapDispatchToProps)(ExpensesUpload))); diff --git a/src/scenes/Moves/Ppm/PPMPaymentRequest.css b/src/scenes/Moves/Ppm/PPMPaymentRequest.css deleted file mode 100644 index 3d4b0f8d578..00000000000 --- a/src/scenes/Moves/Ppm/PPMPaymentRequest.css +++ /dev/null @@ -1,168 +0,0 @@ -.ppm-payment-req-intro .title, -.allowable-expenses-container .title, -.weight-ticket-example-container .title { - font-size: 2.2rem; -} - -.weight-ticket-example-container .subheader { - margin-bottom: 10px; - font-weight: bold; -} - -.weight-ticket-example-container p { - margin: 0; -} - -.trailer-criteria-container .title { - font-size: 2.2rem; - margin-bottom: 0; -} - -.trailer-criteria-container .list-header { - margin: 25px 0 25px 0; - padding-left: 0; -} - -.trailer-criteria-container p { - margin-bottom: 5px; - margin-top: 0; -} - -.weight-ticket-example-img { - vertical-align: middle; -} - -.dashed-divider { - margin-top: 1rem; - border-bottom: 1px dashed #e9e8e8; -} - -.ppm-payment-req-intro a:visited { - color: #0071bc; /* original link color */ -} - -.color_blue_link { - color: #205493; -} - -.ppm-payment-request-footer { - border-top: 1px solid black; - display: flex; - justify-content: space-between; - flex-direction: row; - border-top: 1px solid black; - margin-top: 2.3rem; - padding-top: 1.5rem; - width: 100%; -} - -@media only screen and (max-width: 481px) { - .ppm-payment-request-footer { - flex-direction: column; - } - - .ppm-payment-request-footer .usa-button-secondary { - width: 50%; - margin-left: 0px; - } - - .ppm-payment-request-footer .body--heading { - flex-wrap: wrap; - } -} - -.allowable-expenses-container li { - margin-bottom: 0px; -} - -.allowable-expenses-container p { - line-height: normal; - margin-bottom: 1em; - margin-top: 0.5em; -} - -.allowable-expenses-container .icon { - margin-left: 0px; -} - -.button-bar { - border-top: 1px solid black; - margin-top: 2.3rem; - padding-top: 1rem; -} - -.text-gray { - color: #767676; -} - -.secondary-label { - font-size: 1.5rem; -} - -.dashed-divider { - border-bottom: 1px dashed #e9e8e8; - padding: 1rem; -} - -.radio-group-header { - margin-top: 1em; - margin-bottom: 0.5em; -} - -.radio-group-wrapper { - margin-bottom: 2em; -} - -.short-field { - max-width: 46rem; - display: inline-block; - margin-right: 0px; -} - -.uploader-wrapper { - padding-bottom: 2em; -} - -.uploader-label { - font-size: 1.4em; -} - -.full-weight-label { - margin-top: 0px; -} - -.input-header { - display: block; -} - -.car-img { - height: 15px; -} - -.normalize-margins { - margin-top: 0px; - margin-bottom: 0px; -} - -/* Note: .usa-grid-one-half almost the same except it floats left */ -.one-half { - width: 50%; -} - -@media only screen and (max-width: 481px) { - .one-half { - width: 100%; - } -} - -.input-group .usa-input-error { - margin-top: 0px; -} - -.divider { - margin-bottom: 1.5em; -} - -.bullet-li-header { - margin-bottom: 0.5em; -} diff --git a/src/scenes/Moves/Ppm/PPMPaymentRequestActionBtns.jsx b/src/scenes/Moves/Ppm/PPMPaymentRequestActionBtns.jsx deleted file mode 100644 index 0f473c0d50e..00000000000 --- a/src/scenes/Moves/Ppm/PPMPaymentRequestActionBtns.jsx +++ /dev/null @@ -1,99 +0,0 @@ -import React, { Component } from 'react'; - -// import { get} from 'lodash'; -import './PPMPaymentRequest.css'; -import AlertWithConfirmation from 'shared/AlertWithConfirmation'; -import withRouter from 'utils/routing'; - -class PPMPaymentRequestActionBtns extends Component { - state = { - hasConfirmation: this.props.hasConfirmation, - displayConfirmation: false, - }; - - showConfirmationOrFinishLater = (formValues) => { - const { - router: { navigate }, - hasConfirmation, - } = this.props; - - if (!hasConfirmation) { - return navigate('/ppm'); - } - - this.setState({ displayConfirmation: true }); - return undefined; - }; - - cancelConfirmationHandler = () => { - this.setState({ displayConfirmation: false }); - }; - - confirmFinishLater = () => { - const { - router: { navigate }, - } = this.props; - navigate('/ppm'); - }; - - render() { - const { - nextBtnLabel, - displaySkip, - skipHandler, - saveAndAddHandler, - hasConfirmation, - submitButtonsAreDisabled, - submitting, - } = this.props; - return ( -
-
- {hasConfirmation && this.state.displayConfirmation && ( -
- -
- )} - - {!this.state.displayConfirmation && ( -
- - {displaySkip && ( - - )} - -
- )} -
-
- ); - } -} - -export default withRouter(PPMPaymentRequestActionBtns); diff --git a/src/scenes/Moves/Ppm/PaymentReview/DocumentsUploaded.js b/src/scenes/Moves/Ppm/PaymentReview/DocumentsUploaded.js deleted file mode 100644 index 9597a139640..00000000000 --- a/src/scenes/Moves/Ppm/PaymentReview/DocumentsUploaded.js +++ /dev/null @@ -1,170 +0,0 @@ -import React, { Component } from 'react'; -import { bool } from 'prop-types'; -import { Link } from 'react-router-dom'; -import { connect } from 'react-redux'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; - -import { formatExpenseDocs } from '../utility'; - -import WeightTicketListItem from './WeightTicketListItem'; -import ExpenseTicketListItem from './ExpenseTicketListItem'; - -import { selectPPMCloseoutDocumentsForMove } from 'shared/Entities/modules/movingExpenseDocuments'; -import { deleteMoveDocument, getMoveDocumentsForMove } from 'shared/Entities/modules/moveDocuments'; -import docsAddedCheckmarkImg from 'shared/images/docs_added_checkmark.png'; - -import './PaymentReview.css'; - -export class DocumentsUploaded extends Component { - state = { - showDocs: false, - }; - - static propTypes = { - showLinks: bool, - inReviewPage: bool, - }; - - static defaultProps = { - showLinks: false, - inReviewPage: false, - }; - - componentDidMount() { - const { moveId } = this.props; - this.props.getMoveDocumentsForMove(moveId); - } - - toggleShowDocs = () => { - this.setState((prevState) => ({ showDocs: !prevState.showDocs })); - }; - - renderHeader = () => { - const { expenseDocs, weightTicketSetDocs, weightTicketDocs, inReviewPage } = this.props; - const totalDocs = expenseDocs.length + weightTicketSetDocs.length + weightTicketDocs.length; - const documentLabel = `document${totalDocs > 1 ? 's' : ''}`; - - return

{inReviewPage ? `Document Summary - ${totalDocs} total` : `${totalDocs} ${documentLabel} added`}

; - }; - - render() { - const { showDocs } = this.state; - const { expenseDocs, weightTicketSetDocs, weightTicketDocs, moveId, showLinks, inReviewPage, deleteMoveDocument } = - this.props; - const totalDocs = expenseDocs.length + weightTicketSetDocs.length + weightTicketDocs.length; - const expandedDocumentList = showDocs || inReviewPage; - const hiddenDocumentList = !inReviewPage && !showDocs; - - if (totalDocs === 0) { - return null; - } - return ( -
-
- {!inReviewPage && ( - documents added checkmark - )} - {this.renderHeader()} - {!inReviewPage && ( - - {showDocs ? 'Hide' : 'Show'} - - )} -
- {expandedDocumentList && ( - <> - {weightTicketDocs.length > 0 && ( - <> -

{weightTicketDocs.length} weight tickets

-
- {weightTicketDocs.map((ticket, index) => ( - - ))} -
-
- - )} -

{weightTicketSetDocs.length} sets of weight tickets

-
- {weightTicketSetDocs.map((ticket, index) => ( - - ))} -
- {showLinks && ( - - Add weight ticket - - )} -
-

- {expenseDocs.length} expense{expenseDocs.length >= 0 ? 's' : ''} -

-
- {formatExpenseDocs(expenseDocs).map((expense) => ( - - ))} -
- {showLinks && ( -
- - Add expense - -
- )} - - )} -
- ); - } -} - -function mapStateToProps(state, { moveId }) { - return { - moveId, - expenseDocs: selectPPMCloseoutDocumentsForMove(state, moveId, ['EXPENSE']), - weightTicketSetDocs: selectPPMCloseoutDocumentsForMove(state, moveId, ['WEIGHT_TICKET_SET']), - weightTicketDocs: selectPPMCloseoutDocumentsForMove(state, moveId, ['WEIGHT_TICKET']), - }; -} - -const mapDispatchToProps = { - selectPPMCloseoutDocumentsForMove, - getMoveDocumentsForMove, - deleteMoveDocument, -}; - -export default connect(mapStateToProps, mapDispatchToProps)(DocumentsUploaded); diff --git a/src/scenes/Moves/Ppm/PaymentReview/DocumentsUploaded.test.js b/src/scenes/Moves/Ppm/PaymentReview/DocumentsUploaded.test.js deleted file mode 100644 index 064bb60a0fe..00000000000 --- a/src/scenes/Moves/Ppm/PaymentReview/DocumentsUploaded.test.js +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; -import { mount } from 'enzyme'; - -import { DocumentsUploaded } from './DocumentsUploaded'; - -const initialProps = { - moveId: 0, - expenseDocs: [], - weightTicketDocs: [], - weightTicketSetDocs: [], - getMoveDocumentsForMove: jest.fn(), -}; -function generateWrapper(props) { - return mount(); -} - -describe('DocumentsUploaded Alert', () => { - const documentsUploadedContainer = '[data-testid="documents-uploaded"]'; - describe('No documents uploaded', () => { - it('component does not render', () => { - const wrapper = generateWrapper(); - expect(wrapper.find(documentsUploadedContainer).length).toEqual(0); - }); - }); - - describe('One document uploaded', () => { - it('component renders', () => { - const mockGetMoveDocumentsForMove = jest.fn(); - const wrapper = generateWrapper({ - getMoveDocumentsForMove: mockGetMoveDocumentsForMove, - expenseDocs: [{}], - }); - expect(mockGetMoveDocumentsForMove).toHaveBeenCalled(); - expect(wrapper.find(documentsUploadedContainer).length).toEqual(1); - expect(wrapper.find(documentsUploadedContainer).text()).toContain('1 document added'); - }); - }); - describe('More than one document uploaded', () => { - it('component renders and text uses "documents" instead of "document"', () => { - const mockGetMoveDocumentsForMove = jest.fn(); - const wrapper = generateWrapper({ - getMoveDocumentsForMove: mockGetMoveDocumentsForMove, - expenseDocs: [{}], - weightTicketSetDocs: [{}], - }); - - expect(mockGetMoveDocumentsForMove).toHaveBeenCalled(); - expect(wrapper.find(documentsUploadedContainer).length).toEqual(1); - expect(wrapper.find(documentsUploadedContainer).text()).toContain('2 documents added'); - }); - }); -}); diff --git a/src/scenes/Moves/Ppm/PaymentReview/ExpenseTicketListItem.jsx b/src/scenes/Moves/Ppm/PaymentReview/ExpenseTicketListItem.jsx deleted file mode 100644 index 159fd3090d9..00000000000 --- a/src/scenes/Moves/Ppm/PaymentReview/ExpenseTicketListItem.jsx +++ /dev/null @@ -1,84 +0,0 @@ -import React, { Component } from 'react'; -import { forEach } from 'lodash'; -import { string } from 'prop-types'; - -import deleteButtonImg from 'shared/images/delete-doc-button.png'; -import AlertWithDeleteConfirmation from 'shared/AlertWithDeleteConfirmation'; -import { UPLOAD_SCAN_STATUS } from 'shared/constants'; - -class ExpenseTicketListItem extends Component { - state = { - showDeleteConfirmation: false, - }; - - areUploadsInfected = (uploads) => { - let isInfected = false; - forEach(uploads, function (upload) { - if (upload.status === UPLOAD_SCAN_STATUS.INFECTED) { - isInfected = true; - } - }); - return isInfected; - }; - - toggleShowConfirmation = () => { - const { showDeleteConfirmation } = this.state; - this.setState({ showDeleteConfirmation: !showDeleteConfirmation }); - }; - - render() { - const { id, amount, type, paymentMethod, showDelete, deleteDocumentListItem, uploads } = this.props; - const { showDeleteConfirmation } = this.state; - const isInfected = this.areUploadsInfected(uploads); - return ( -
-
-
-

- {type} - ${amount} -

- {showDelete && ( - delete document button - )} -
- {isInfected && ( -
- Delete this file, take a photo of the document, then upload that -
- )} -
- {type} ({paymentMethod === 'OTHER' ? 'Not GTCC' : paymentMethod}) -
- - {showDeleteConfirmation && ( - deleteDocumentListItem(id)} - cancelActionHandler={this.toggleShowConfirmation} - type="expense-ticket-list-alert" - /> - )} -
-
- ); - } -} - -ExpenseTicketListItem.propTypes = { - id: string.isRequired, - amount: string.isRequired, - type: string.isRequired, - paymentMethod: string.isRequired, -}; - -ExpenseTicketListItem.defaultProps = { - showDelete: false, -}; - -export default ExpenseTicketListItem; diff --git a/src/scenes/Moves/Ppm/PaymentReview/PaymentReview.css b/src/scenes/Moves/Ppm/PaymentReview/PaymentReview.css deleted file mode 100644 index 8d5ff8889ed..00000000000 --- a/src/scenes/Moves/Ppm/PaymentReview/PaymentReview.css +++ /dev/null @@ -1,122 +0,0 @@ -.payment-review-container h3, -.payment-review-container h4, -.payment-review-container h5, -.payment-review-container p { - line-height: normal; - margin: 0; -} - -.payment-review-container input[type='image'], -.payment-review-container input[type='image']:hover { - padding: 0; - height: 20px; -} - -.doc-summary-container { - background-color: #e7f4e4; - border-left: 10px solid #2e8540; - padding-left: 2em; - padding-top: 1em; -} - -/* normalize margins of all children */ -.doc-summary-container * { - margin-top: 0px; - margin-left: 0px; - margin-bottom: 0px; - margin-right: 0px; -} - -.doc-summary-container h3 { - font-size: 2rem; -} - -.doc-summary-container > h4 { - margin-top: 0.5rem; -} - -.review-payment-request-header h3 { - margin-top: 2rem; -} - -.review-payment-request-header p { - padding: 1rem 0 2rem 0; -} - -.ticket-item { - width: 98%; - border-bottom: 1px dashed; - padding: 1.5rem 0 1.5rem 0; -} - -.ticket-item:last-child { - border-bottom: none; -} - -.weight-ticket-image { - max-height: 25px; -} - -#doc-summary-separator { - max-width: 98%; - margin-top: 12px; -} - -.add-expense-link { - padding-bottom: 1rem; -} - -.doc-summary-container a:visited { - text-decoration: none; - color: #0071bc; -} - -.doc-review { - margin-top: 2rem; -} - -.missing-label { - color: #cd3504; -} - -.expense-li-item-container { - display: flex; - justify-content: space-between; - max-width: 916px; -} - -.weight-li-item-container { - display: flex; - justify-content: space-between; - max-width: 820px; -} - -.infected-indicator { - color: #cd3504; -} - -.review-customer-agreement-container { - margin: 1.5em 0 4.5em 0; -} - -.review-customer-agreement { - background-color: #ededed; - padding: 1em; -} - -.review-customer-agreement p { - margin-top: 0; - margin-bottom: 0; - padding-bottom: 1rem; -} - -.review-customer-agreement label { - margin-top: 0; - margin-bottom: 0; -} - -.documents-uploaded-header { - display: flex; - align-items: baseline; - margin-right: 5; -} diff --git a/src/scenes/Moves/Ppm/PaymentReview/WeightTicketListItem.jsx b/src/scenes/Moves/Ppm/PaymentReview/WeightTicketListItem.jsx deleted file mode 100644 index c0acad9e8ed..00000000000 --- a/src/scenes/Moves/Ppm/PaymentReview/WeightTicketListItem.jsx +++ /dev/null @@ -1,155 +0,0 @@ -import React, { Component } from 'react'; -import { forEach } from 'lodash'; -import { string, number, bool } from 'prop-types'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; - -import carImg from 'shared/images/car_mobile.png'; -import boxTruckImg from 'shared/images/box_truck_mobile.png'; -import carTrailerImg from 'shared/images/car-trailer_mobile.png'; -import { formatToOrdinal } from 'utils/formatters'; -import deleteButtonImg from 'shared/images/delete-doc-button.png'; -import AlertWithDeleteConfirmation from 'shared/AlertWithDeleteConfirmation'; -import { UPLOAD_SCAN_STATUS, WEIGHT_TICKET_SET_TYPE } from 'shared/constants'; - -const WEIGHT_TICKET_IMAGES = { - CAR: carImg, - BOX_TRUCK: boxTruckImg, - CAR_TRAILER: carTrailerImg, -}; - -const MissingLabel = ({ children }) => ( -

- {children} -

-); - -class WeightTicketListItem extends Component { - state = { - showDeleteConfirmation: false, - }; - - areUploadsInfected = (uploads) => { - let isInfected = false; - forEach(uploads, function (upload) { - if (upload.status === UPLOAD_SCAN_STATUS.INFECTED) { - isInfected = true; - } - }); - return isInfected; - }; - - toggleShowConfirmation = () => { - const { showDeleteConfirmation } = this.state; - this.setState({ showDeleteConfirmation: !showDeleteConfirmation }); - }; - - render() { - const { - id, - empty_weight_ticket_missing, - empty_weight, - full_weight_ticket_missing, - full_weight, - num, - trailer_ownership_missing, - vehicle_nickname, - vehicle_make, - vehicle_model, - weight_ticket_set_type, - showDelete, - deleteDocumentListItem, - isWeightTicketSet, - uploads, - } = this.props; - const { showDeleteConfirmation } = this.state; - const isInfected = this.areUploadsInfected(uploads); - const showWeightTicketIcon = weight_ticket_set_type !== 'PRO_GEAR'; - const showVehicleNickname = weight_ticket_set_type === 'BOX_TRUCK' || 'PRO_GEAR'; - const showVehicleMakeModel = weight_ticket_set_type === 'CAR' || 'CAR_TRAILER'; - return ( -
- {/* size of largest of the images */} -
- {showWeightTicketIcon && ( - {weight_ticket_set_type} - )} -
-
-
-

- {showVehicleMakeModel && ( - <> - {vehicle_make} {vehicle_model} - - )} - {showVehicleNickname && <>{vehicle_nickname} } - {isWeightTicketSet && <>{formatToOrdinal(num + 1)} set} -

- {showDelete && ( - delete document button - )} -
- {isInfected && ( -
- Delete this file, take a photo of the document, then upload that -
- )} - {empty_weight_ticket_missing ? ( - - Missing empty weight ticket{' '} - - - ) : ( -

Empty weight ticket {empty_weight} lbs

- )} - {full_weight_ticket_missing ? ( - - Missing full weight ticket{' '} - - - ) : ( -

Full weight ticket {full_weight} lbs

- )} - {weight_ticket_set_type === WEIGHT_TICKET_SET_TYPE.CAR_TRAILER && trailer_ownership_missing && ( - - Missing ownership documentation{' '} - - - )} - {weight_ticket_set_type === WEIGHT_TICKET_SET_TYPE.CAR_TRAILER && !trailer_ownership_missing && ( -

Ownership documentation

- )} - {showDeleteConfirmation && ( - deleteDocumentListItem(id)} - cancelActionHandler={this.toggleShowConfirmation} - type="weight-ticket-list-alert" - /> - )} -
-
- ); - } -} - -WeightTicketListItem.propTypes = { - id: string.isRequired, - num: number.isRequired, - isWeightTicketSet: bool.isRequired, -}; - -WeightTicketListItem.defaultProps = { - showDelete: false, -}; -export default WeightTicketListItem; diff --git a/src/scenes/Moves/Ppm/TrailerCriteria.jsx b/src/scenes/Moves/Ppm/TrailerCriteria.jsx deleted file mode 100644 index cc85f2ed8aa..00000000000 --- a/src/scenes/Moves/Ppm/TrailerCriteria.jsx +++ /dev/null @@ -1,48 +0,0 @@ -import React from 'react'; -import { useNavigate } from 'react-router-dom'; - -function TrailerCriteria() { - const navigate = useNavigate(); - function goBack() { - navigate(-1); - } - return ( -
- -

Trailer Criteria

-
-

- During your move, if you used a trailer owned by you or your spouse, you can claim its weight{' '} - once per move if it meets these specifications: -

-
-

A utility trailer:

-
    -
  • With or without a tilt bed
  • -
  • Single axle
  • -
  • No more than 12 feet long from rear to trailer hitch
  • -
  • No more than 8 feet wide from outside tire to outside tire
  • -
  • Side rails and body no higher than 28 inches (unless detachable)
  • -
  • Ramp or gate for the utility trailer no higher than 4 feet (unless detachable)
  • -
-
-
-

- You will also have to provide proof of ownership, either a registration or bill of sale. If these are - unavailable in your state, you can provide a signed and dated statement certifying that you or your spouse own - the trailer. -

-
- -
-
- ); -} - -export default TrailerCriteria; diff --git a/src/scenes/Moves/Ppm/WeightTicket.jsx b/src/scenes/Moves/Ppm/WeightTicket.jsx deleted file mode 100644 index 52604a96b4b..00000000000 --- a/src/scenes/Moves/Ppm/WeightTicket.jsx +++ /dev/null @@ -1,578 +0,0 @@ -import React, { Component } from 'react'; -import { getFormValues, reduxForm } from 'redux-form'; -import { connect } from 'react-redux'; -import { get, map } from 'lodash'; -import PropTypes from 'prop-types'; -import { Link } from 'react-router-dom'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; - -import WizardHeader from '../WizardHeader'; - -import { getNextPage } from './utility'; -import DocumentsUploaded from './PaymentReview/DocumentsUploaded'; -import PPMPaymentRequestActionBtns from './PPMPaymentRequestActionBtns'; - -import { ProgressTimeline, ProgressTimelineStep } from 'shared/ProgressTimeline'; -import { SwaggerField } from 'shared/JsonSchemaForm/JsonSchemaField'; -import RadioButton from 'shared/RadioButton'; -import Checkbox from 'shared/Checkbox'; -import Uploader from 'shared/Uploader'; -import Alert from 'shared/Alert'; -import { formatDateForSwagger } from 'shared/dates'; -import { documentSizeLimitMsg, WEIGHT_TICKET_SET_TYPE } from 'shared/constants'; -import { selectCurrentPPM, selectServiceMemberFromLoggedInUser } from 'store/entities/selectors'; -import carTrailerImg from 'shared/images/car-trailer_mobile.png'; -import carImg from 'shared/images/car_mobile.png'; -import { createWeightTicketSetDocument } from 'shared/Entities/modules/weightTicketSetDocuments'; -import { selectPPMCloseoutDocumentsForMove } from 'shared/Entities/modules/movingExpenseDocuments'; -import { getMoveDocumentsForMove } from 'shared/Entities/modules/moveDocuments'; -import { withContext } from 'shared/AppContext'; -import { formatToOrdinal } from 'utils/formatters'; - -import './PPMPaymentRequest.css'; -import withRouter from 'utils/routing'; -import { RouterShape } from 'types'; - -const nextBtnLabels = { - SaveAndAddAnother: 'Save & Add Another', - SaveAndContinue: 'Save & Continue', -}; - -const reviewPagePath = '/ppm-payment-review'; -const nextPagePath = '/ppm-expenses-intro'; - -const uploadEmptyTicketLabel = - 'Drag & drop or click to upload empty weight ticket'; -const uploadFullTicketLabel = - 'Drag & drop or click to upload full weight ticket'; -const uploadTrailerProofOfOwnership = - 'Drag & drop or click to upload documentation'; - -class WeightTicket extends Component { - state = { ...this.initialState }; - - uploaders = { - trailer: { uploaderRef: null, isMissingChecked: () => this.state.missingDocumentation }, - emptyWeight: { uploaderRef: null, isMissingChecked: () => this.state.missingEmptyWeightTicket }, - fullWeight: { uploaderRef: null, isMissingChecked: () => this.state.missingFullWeightTicket }, - }; - - get initialState() { - return { - weightTicketSetType: '', - additionalWeightTickets: 'No', - isValidTrailer: 'No', - weightTicketSubmissionError: false, - missingDocumentation: false, - missingEmptyWeightTicket: false, - missingFullWeightTicket: false, - }; - } - - componentDidMount() { - const { moveId } = this.props; - this.props.getMoveDocumentsForMove(moveId); - } - - get isCarTrailer() { - return this.state.weightTicketSetType === WEIGHT_TICKET_SET_TYPE.CAR_TRAILER; - } - - get isCar() { - return this.state.weightTicketSetType === WEIGHT_TICKET_SET_TYPE.CAR; - } - - get isProGear() { - return this.state.weightTicketSetType === WEIGHT_TICKET_SET_TYPE.PRO_GEAR; - } - - hasWeightTicket = (uploaderRef) => { - return !!(uploaderRef && !uploaderRef.isEmpty()); - }; - - invalidState = (uploader) => { - if (uploader.isMissingChecked()) { - return true; - } - return !this.hasWeightTicket(uploader.uploaderRef); - }; - - carTrailerText = (isValidTrailer) => { - if (this.isCarTrailer && isValidTrailer === 'Yes') { - return ( -
- You can claim this trailer's weight as part of the total weight of your trip. -
- ); - } - if (this.isCarTrailer) { - return ( -
- The weight of this trailer should be excluded from the total weight of this trip. -

{documentSizeLimitMsg}

-
- ); - } - // if there is no trailer, don't show text - return undefined; - }; - - uploaderWithInvalidState = () => { - // Validation for the vehicle type - if (this.state.isValidTrailer === 'Yes' && this.isCarTrailer && this.invalidState(this.uploaders.trailer)) { - return true; - } - // Full weight must be in a valid state to proceed. - return this.invalidState(this.uploaders.fullWeight); - }; - - // handleChange for weightTicketSetType and additionalWeightTickets - handleChange = (event, type) => { - this.setState({ [type]: event.target.value }); - }; - - handleCheckboxChange = (event) => { - this.setState({ - [event.target.name]: event.target.checked, - }); - }; - - onAddFile = (uploaderName) => () => { - this.setState((prevState) => ({ - uploaderIsIdle: { ...prevState.uploaderIsIdle, [uploaderName]: false }, - })); - }; - - onUploadChange = (uploaderName) => (uploaderIsIdle) => { - this.setState((prevState) => ({ - uploaderIsIdle: { ...prevState.uploaderIsIdle, [uploaderName]: uploaderIsIdle }, - })); - }; - - skipHandler = () => { - const { - moveId, - router: { navigate }, - } = this.props; - navigate(`/moves/${moveId}${nextPagePath}`); - }; - - nonEmptyUploaderKeys() { - const uploadersKeys = Object.keys(this.uploaders); - return uploadersKeys.filter((key) => this.uploaders[key].uploaderRef && !this.uploaders[key].uploaderRef.isEmpty()); - } - - saveAndAddHandler = (formValues) => { - const { - moveId, - currentPpm, - router: { navigate }, - } = this.props; - const { additionalWeightTickets } = this.state; - - const uploaderKeys = this.nonEmptyUploaderKeys(); - const uploadIds = []; - for (const key of uploaderKeys) { - const files = this.uploaders[key].uploaderRef.getFiles(); - const documentUploadIds = map(files, 'id'); - uploadIds.push(...documentUploadIds); - } - const weightTicketSetDocument = { - personally_procured_move_id: currentPpm.id, - upload_ids: uploadIds, - weight_ticket_set_type: formValues.weight_ticket_set_type, - vehicle_nickname: formValues.vehicle_nickname, - vehicle_make: formValues.vehicle_make, - vehicle_model: formValues.vehicle_model, - empty_weight_ticket_missing: this.state.missingEmptyWeightTicket, - empty_weight: formValues.empty_weight, - full_weight_ticket_missing: this.state.missingFullWeightTicket, - full_weight: formValues.full_weight, - weight_ticket_date: formatDateForSwagger(formValues.weight_ticket_date), - trailer_ownership_missing: this.state.missingDocumentation, - move_document_type: 'WEIGHT_TICKET_SET', - notes: formValues.notes, - }; - return this.props - .createWeightTicketSetDocument(moveId, weightTicketSetDocument) - .then(() => { - this.cleanup(); - if (additionalWeightTickets === 'No') { - const nextPage = getNextPage(`/moves/${moveId}${nextPagePath}`, this.props.lastLocation, reviewPagePath); - navigate(nextPage); - } - }) - .catch((e) => { - this.setState({ weightTicketSubmissionError: true }); - }); - }; - - cleanup = () => { - const { reset } = this.props; - const { uploaders } = this; - const uploaderKeys = this.nonEmptyUploaderKeys(); - for (const key of uploaderKeys) { - uploaders[key].uploaderRef.clearFiles(); - } - reset(); - this.setState({ ...this.initialState }); - }; - - render() { - const { - additionalWeightTickets, - weightTicketSetType, - missingEmptyWeightTicket, - missingFullWeightTicket, - missingDocumentation, - isValidTrailer, - } = this.state; - const { handleSubmit, submitting, schema, weightTicketSets, invalid, moveId, transportationOffice } = this.props; - const nextBtnLabel = - additionalWeightTickets === 'Yes' ? nextBtnLabels.SaveAndAddAnother : nextBtnLabels.SaveAndContinue; - const weightTicketSetOrdinal = formatToOrdinal(weightTicketSets.length + 1); - const fullWeightTicketFieldsRequired = missingFullWeightTicket ? null : true; - const emptyWeightTicketFieldsRequired = missingEmptyWeightTicket ? null : true; - - return ( -
- - - - - - } - /> -
-
- -
-
-
-
-
- {this.state.weightTicketSubmissionError && ( -
-
- - Something went wrong contacting the server. - -
-
- )} -
-

Weight Tickets - {weightTicketSetOrdinal} set

- Upload weight tickets for each vehicle trip and pro-gear weigh.{' '} - - - - this.handleChange(event, 'weightTicketSetType')} - value={weightTicketSetType} - required - /> - {weightTicketSetType && - (this.isCarTrailer || this.isCar ? ( - <> - - - - ) : ( - - ))} - {weightTicketSetType && this.isCarTrailer && ( - <> -
-

- Do you own this trailer, and does it meet all{' '} - - trailer criteria - - ? -

- this.handleChange(event, 'isValidTrailer')} - /> - - this.handleChange(event, 'isValidTrailer')} - /> -
- {isValidTrailer === 'Yes' && ( - <> -

- Proof of ownership (ex. registration, bill of sale) -

-

{documentSizeLimitMsg}

- - (this.uploaders.trailer.uploaderRef = ref)} - onChange={this.onUploadChange('trailer')} - onAddFile={this.onAddFile('trailer')} - /> - - - {missingDocumentation && ( -
-
- - If your state does not provide a registration or bill of sale for your trailer, you may - write and upload a signed and dated statement certifying that you or your spouse own the - trailer and meets the{' '} - - trailer criteria - - . Upload your statement using the proof of ownership field. - -
-
- )} - - )} - - )} - {weightTicketSetType && ( - <> -
- -
-
- {this.carTrailerText(isValidTrailer)} -
-
- - Empty Weight{' '} - {this.isCarTrailer && - (isValidTrailer === 'Yes' ? ( - <> - ( car only car only) - - ) : ( - <> - ( car and trailer car + - trailer) - - ))} - - {' '} - lbs -
-
- - (this.uploaders.emptyWeight.uploaderRef = ref)} - onChange={this.onUploadChange('emptyWeight')} - onAddFile={this.onAddFile('emptyWeight')} - /> - - - {missingEmptyWeightTicket && ( - - - Contact your local Transportation Office (PPPO) to let them know you’re missing this - weight ticket. For now, keep going and enter the info you do have. - - - )} -
-
-
-
-
-
- - Full Weight{' '} - {this.isCarTrailer && ( - <> - ( car and trailer car + trailer) - - )} - - - {' '} - lbs -
- -
-
- (this.uploaders.fullWeight.uploaderRef = ref)} - onChange={this.onUploadChange('fullWeight')} - onAddFile={this.onAddFile('fullWeight')} - /> -
- - {missingFullWeightTicket && ( -
- - You can’t get paid without a full weight ticket. See what you can do to find it, - because without certified documentation of the weight of your belongings, we can’t pay you - your incentive. Call the {transportationOffice.name} Transportation Office at{' '} - {transportationOffice.phone_lines[0]} if you have any questions. - -
- )} -
-
- - -
- -
-

Do you have more weight tickets for another vehicle or trip?

- this.handleChange(event, 'additionalWeightTickets')} - /> - - this.handleChange(event, 'additionalWeightTickets')} - /> -
- - )} - = 1} - /> -
- -
-
-
- ); - } -} - -const formName = 'weight_ticket_wizard'; -WeightTicket = reduxForm({ - form: formName, - enableReinitialize: true, - keepDirtyOnReinitialize: true, -})(WeightTicket); - -WeightTicket.propTypes = { - schema: PropTypes.object.isRequired, - router: RouterShape, -}; - -function mapStateToProps(state, ownProps) { - const { - router: { - params: { moveId }, - }, - } = ownProps; - const serviceMember = selectServiceMemberFromLoggedInUser(state); - const dutyLocationId = serviceMember?.current_location?.id; - const transportationOffice = serviceMember?.current_location.transportation_office; - - return { - moveId, - formValues: getFormValues(formName)(state), - genericMoveDocSchema: get(state, 'swaggerInternal.spec.definitions.CreateGenericMoveDocumentPayload', {}), - moveDocSchema: get(state, 'swaggerInternal.spec.definitions.MoveDocumentPayload', {}), - schema: get(state, 'swaggerInternal.spec.definitions.CreateWeightTicketDocumentsPayload', {}), - currentPpm: selectCurrentPPM(state) || {}, - weightTicketSets: selectPPMCloseoutDocumentsForMove(state, moveId, ['WEIGHT_TICKET_SET']), - transportationOffice, - dutyLocationId, - }; -} - -const mapDispatchToProps = { - getMoveDocumentsForMove, - createWeightTicketSetDocument, -}; - -export default withContext(withRouter(connect(mapStateToProps, mapDispatchToProps)(WeightTicket))); diff --git a/src/scenes/Moves/Ppm/WeightTicket.test.js b/src/scenes/Moves/Ppm/WeightTicket.test.js deleted file mode 100644 index b9ddbef50b5..00000000000 --- a/src/scenes/Moves/Ppm/WeightTicket.test.js +++ /dev/null @@ -1,162 +0,0 @@ -import React from 'react'; -import { mount } from 'enzyme'; - -import WeightTicket from './WeightTicket'; -import PPMPaymentRequestActionBtns from './PPMPaymentRequestActionBtns'; - -import { MockProviders } from 'testUtils'; - -function mountComponents( - moreWeightTickets = 'Yes', - formInvalid, - uploaderWithInvalidState, - weightTicketSetType = 'CAR', -) { - const initialState = { - emptyWeight: 1100, - fullWeight: 2000, - weightTicketSetType, - weightTicketDate: '2019-05-22', - }; - const params = { moveId: 'someID' }; - const wrapper = mount( - - - , - ); - const wt = wrapper.find('WeightTicket'); - if (formInvalid !== undefined) { - wt.instance().invalid = jest.fn().mockReturnValue(formInvalid); - wt.instance().uploaderWithInvalidState = jest.fn().mockReturnValue(uploaderWithInvalidState); - } - wt.setState({ additionalWeightTickets: moreWeightTickets, ...initialState }); - wt.update(); - return wrapper.find('WeightTicket'); -} - -describe('Weight tickets page', () => { - describe('Service member is missing a weight ticket', () => { - it('renders both the Save buttons are disabled', () => { - const weightTicket = mountComponents('No', true, true); - const buttonGroup = weightTicket.find(PPMPaymentRequestActionBtns); - const finishLater = weightTicket.find('button').at(0); - const saveAndAdd = weightTicket.find('button').at(1); - - expect(buttonGroup.length).toEqual(1); - expect(saveAndAdd.props().disabled).toEqual(true); - expect(finishLater.props().disabled).not.toEqual(true); - }); - }); - describe('Service member chooses CAR as weight ticket type', () => { - it('renders vehicle make and model fields', () => { - const weightTicket = mountComponents('No', true, true, 'CAR'); - const vehicleNickname = weightTicket.find('[data-testid="vehicle_nickname"]'); - const vehicleMake = weightTicket.find('[data-testid="vehicle_make"]'); - const vehicleModel = weightTicket.find('[data-testid="vehicle_model"]'); - - expect(vehicleNickname.length).toEqual(0); - expect(vehicleMake.length).toEqual(1); - expect(vehicleModel.length).toEqual(1); - }); - }); - describe('Service member chooses BOX TRUCK as weight ticket type', () => { - it('renders vehicle nickname field', () => { - const weightTicket = mountComponents('No', true, true, 'BOX_TRUCK'); - const vehicleNickname = weightTicket.find('[data-testid="vehicle_nickname"]'); - const vehicleMake = weightTicket.find('[data-testid="vehicle_make"]'); - const vehicleModel = weightTicket.find('[data-testid="vehicle_model"]'); - - expect(vehicleNickname.length).toEqual(1); - expect(vehicleMake.length).toEqual(0); - expect(vehicleModel.length).toEqual(0); - }); - }); - describe('Service member chooses PROGEAR as weight ticket type', () => { - it('renders vehicle nickname (progear type) field', () => { - const weightTicket = mountComponents('No', true, true, 'PRO_GEAR'); - const vehicleNickname = weightTicket.find('[data-testid="vehicle_nickname"]'); - const vehicleMake = weightTicket.find('[data-testid="vehicle_make"]'); - const vehicleModel = weightTicket.find('[data-testid="vehicle_model"]'); - - expect(vehicleNickname.length).toEqual(1); - expect(vehicleMake.length).toEqual(0); - expect(vehicleModel.length).toEqual(0); - }); - }); - describe('Service member has uploaded both a weight tickets', () => { - it('renders both the Save buttons are enabled', () => { - const weightTicket = mountComponents('No', false, false); - const buttonGroup = weightTicket.find(PPMPaymentRequestActionBtns); - const finishLater = weightTicket.find('button').at(0); - const saveAndAdd = weightTicket.find('button').at(1); - - expect(buttonGroup.length).toEqual(1); - expect(saveAndAdd.props().disabled).toEqual(false); - expect(finishLater.props().disabled).not.toEqual(true); - }); - }); - describe('Service member answers "Yes" that they have more weight tickets', () => { - it('renders Save and Add Another Button', () => { - const weightTicket = mountComponents('Yes'); - const buttonGroup = weightTicket.find(PPMPaymentRequestActionBtns); - expect(buttonGroup.length).toEqual(1); - expect(buttonGroup.props().nextBtnLabel).toEqual('Save & Add Another'); - }); - }); - describe('Service member answers "No" that they have more weight tickets', () => { - it('renders Save and Add Continue Button', () => { - const weightTicket = mountComponents('No'); - const buttonGroup = weightTicket.find(PPMPaymentRequestActionBtns); - expect(buttonGroup.length).toEqual(1); - expect(buttonGroup.props().nextBtnLabel).toEqual('Save & Continue'); - }); - }); -}); - -describe('uploaderWithInvalidState', () => { - it('returns true when there are no uploaders', () => { - const weightTicket = mountComponents('No'); - const uploaders = { - emptyWeight: { uploaderRef: {} }, - fullWeight: { uploaderRef: {} }, - trailer: { uploaderRef: {} }, - }; - weightTicket.instance().uploaders = uploaders; - uploaders.emptyWeight.uploaderRef.isEmpty = jest.fn(() => false); - uploaders.emptyWeight.isMissingChecked = jest.fn(() => false); - uploaders.fullWeight.uploaderRef.isEmpty = jest.fn(() => false); - uploaders.fullWeight.isMissingChecked = jest.fn(() => false); - - expect(weightTicket.instance().uploaderWithInvalidState()).toEqual(false); - }); - it('returns false when uploaders have at least one file and isMissing is not checked', () => { - const weightTicket = mountComponents('No'); - const uploaders = { - emptyWeight: { uploaderRef: {} }, - fullWeight: { uploaderRef: {} }, - trailer: { uploaderRef: {} }, - }; - uploaders.emptyWeight.uploaderRef.isEmpty = jest.fn(() => false); - uploaders.emptyWeight.isMissingChecked = jest.fn(() => false); - uploaders.fullWeight.uploaderRef.isEmpty = jest.fn(() => false); - uploaders.fullWeight.isMissingChecked = jest.fn(() => false); - weightTicket.instance().uploaders = uploaders; - - expect(weightTicket.instance().uploaderWithInvalidState()).toEqual(false); - }); - it('returns true when uploaders have at least one file and isMissing is checked', () => { - const weightTicket = mountComponents('No'); - const uploaders = { - emptyWeight: { uploaderRef: {} }, - fullWeight: { uploaderRef: {} }, - trailer: { uploaderRef: {} }, - }; - uploaders.emptyWeight.uploaderRef.isEmpty = jest.fn(() => false); - uploaders.emptyWeight.isMissingChecked = jest.fn(() => false); - uploaders.fullWeight.uploaderRef.isEmpty = jest.fn(() => false); - uploaders.fullWeight.isMissingChecked = jest.fn(() => true); - weightTicket.instance().uploaders = uploaders; - - expect(weightTicket.instance().uploaderWithInvalidState()).toEqual(true); - }); -}); diff --git a/src/scenes/Moves/Ppm/WeightTicketExamples.jsx b/src/scenes/Moves/Ppm/WeightTicketExamples.jsx deleted file mode 100644 index f0594f77622..00000000000 --- a/src/scenes/Moves/Ppm/WeightTicketExamples.jsx +++ /dev/null @@ -1,135 +0,0 @@ -import React from 'react'; -import { useNavigate } from 'react-router-dom'; - -import weightTixExample from 'shared/images/weight_tix_example.png'; -import weightScenario1 from 'shared/images/weight_scenario1.png'; -import weightScenario2 from 'shared/images/weight_scenario2.png'; -import weightScenario3 from 'shared/images/weight_scenario3.png'; -import weightScenario4 from 'shared/images/weight_scenario4.png'; -import weightScenario5 from 'shared/images/weight_scenario5.png'; - -function WeightTicketExamples(props) { - const navigate = useNavigate(); - function goBack() { - navigate(-1); - } - return ( -
- -

Example weight ticket scenarios

-
-
- You need two weight tickets for each trip you took: one with the vehicle empty, one with it full. -
- weight ticket example = A{' '} - trip includes both an empty and full weight ticket -
-
-
-
Scenario 1
-

You and your spouse each drove a vehicle filled with stuff to your destination.

-
- weight scenario 1 -
-

- You must upload weight tickets for 2 trips, which is 4 tickets total. -

-

- That's one empty weight ticket and one full weight ticket for each vehicle. -

-
-
-
-
Scenario 2
-

You made two separate trips in one vehicle to bring stuff to your destination.

-
- weight scenario 2 -
-

- You must upload weight tickets for 2 trips, which is 4 tickets total. -

-

- - - That's one empty and one full ticket for the first trip, and one empty and one full for the second trip. - {' '} - You do need to weigh your empty vehicle a second time. - -

-
-
-
-
Scenario 3
-

- You and your spouse each drove a vehicle filled with stuff to your destination. Then you made a second trip in - your vehicle (without your spouse) to bring more stuff. -

-
- weight scenario 3 -
-

- You must upload weight tickets for 3 trips, which is 6 tickets total. -

-

- - That's one empty and one full weight ticket for each vehicle on the first trip, and an empty and full weight - ticket for your vehicle on the second trip. - -

-
-
-
-
Scenario 4
-

- You drove your car with an attached rental trailer to your destination, then made a second trip to bring more - stuff. -

-
- weight scenario 4 -
-

- You must upload weight tickets for 2 trips, which is 4 tickets total. -

-

- - You can't claim the weight of your rented trailer in your move. All weight tickets must include the weight - of your car with trailer attached. - -

-
-
- -
-
Scenario 5
-

- You drove your car with an attached trailer that you own, and that meets the trailer criteria, to make two - separate trips to move stuff to your destination. -

-
- weight scenario 5 -
-

- You must upload weight tickets for 2 trips, which is 4 tickets total. -

-

- - You can claim the weight of your own trailer once per move (not per trip). The empty weight ticket for your - first trip should be the weight of your car only. All 3 additional weight tickets should include the weight - of your car with trailer attached. - -

-
-
- -
-
- ); -} - -export default WeightTicketExamples; diff --git a/src/scenes/Moves/Ppm/api.js b/src/scenes/Moves/Ppm/api.js deleted file mode 100644 index 415cd423dce..00000000000 --- a/src/scenes/Moves/Ppm/api.js +++ /dev/null @@ -1,29 +0,0 @@ -import { getClient, checkResponse } from 'shared/Swagger/api'; -import { formatPayload } from 'shared/utils'; -import { formatDateForSwagger } from 'shared/dates'; - -export async function UpdatePpm( - moveId, - personallyProcuredMoveId, - payload /* shape: {size, weightEstimate, estimatedIncentive} */, -) { - const client = await getClient(); - const payloadDef = client.spec.definitions.PatchPersonallyProcuredMovePayload; - payload.original_move_date = formatDateForSwagger(payload.original_move_date); - const response = await client.apis.ppm.patchPersonallyProcuredMove({ - moveId, - personallyProcuredMoveId, - patchPersonallyProcuredMovePayload: formatPayload(payload, payloadDef), - }); - checkResponse(response, 'failed to update ppm due to server error'); - return response.body; -} - -export async function RequestPayment(personallyProcuredMoveId) { - const client = await getClient(); - const response = await client.apis.ppm.requestPPMPayment({ - personallyProcuredMoveId, - }); - checkResponse(response, 'failed to update ppm status due to server error'); - return response.body; -} diff --git a/src/scenes/Moves/Ppm/utility.js b/src/scenes/Moves/Ppm/utility.js deleted file mode 100644 index 11cd4cb3e2f..00000000000 --- a/src/scenes/Moves/Ppm/utility.js +++ /dev/null @@ -1,33 +0,0 @@ -import { formatCents } from 'utils/formatters'; -import { MOVE_DOC_TYPE } from 'shared/constants'; - -export const getNextPage = (nextPage, lastPage, pageToRevisit) => { - if (lastPage && lastPage.pathname.includes(pageToRevisit)) { - return lastPage.pathname; - } - return nextPage; -}; - -export const formatExpenseType = (expenseType) => { - if (typeof expenseType !== 'string') return ''; - const type = expenseType.toLowerCase().replace('_', ' '); - return type.charAt(0).toUpperCase() + type.slice(1); -}; - -export const formatExpenseDocs = (expenseDocs) => { - return expenseDocs.map((expense) => ({ - id: expense.id, - amount: formatCents(expense.requested_amount_cents), - type: formatExpenseType(expense.moving_expense_type), - paymentMethod: expense.payment_method, - uploads: expense.document.uploads, - })); -}; - -export const calcNetWeight = (documents) => - documents.reduce((accum, { move_document_type, full_weight, empty_weight }) => { - if (move_document_type === MOVE_DOC_TYPE.WEIGHT_TICKET_SET) { - return accum + (full_weight - empty_weight); - } - return accum; - }, 0); diff --git a/src/scenes/Moves/Ppm/utility.test.js b/src/scenes/Moves/Ppm/utility.test.js deleted file mode 100644 index 0d84f022202..00000000000 --- a/src/scenes/Moves/Ppm/utility.test.js +++ /dev/null @@ -1,48 +0,0 @@ -import { getNextPage, calcNetWeight } from './utility'; - -import { MOVE_DOC_TYPE } from 'shared/constants'; - -describe('PPM Utility functions', () => { - describe('getNextPage', () => { - it('returns to the lastPage when user is visiting from pageToRevisit', () => { - const lastPage = { - pathname: 'moves/:moveid/ppm-payment-review', - search: '', - hash: '', - state: undefined, - }; - const nextPage = getNextPage('moves/:moveid/next-page', lastPage, '/ppm-payment-review'); - - expect(nextPage).toEqual('moves/:moveid/ppm-payment-review'); - }); - it('returns to the nextPage when user is not visiting from pageToRevisit', () => { - const lastPage = { - pathname: 'moves/:moveid/some-other-page', - search: '', - hash: '', - state: undefined, - }; - const nextPage = getNextPage('moves/:moveid/next-page', lastPage, '/ppm-payment-review'); - - expect(nextPage).toEqual('moves/:moveid/next-page'); - }); - it('returns to the nextPage when no lastpage', () => { - const nextPage = getNextPage('moves/:moveid/next-page', null, '/ppm-payment-review'); - - expect(nextPage).toEqual('moves/:moveid/next-page'); - }); - }); - describe('calcNetWeight', () => { - it('should return 0 when there are no weight ticket sets', () => { - const documents = [{ move_document_type: MOVE_DOC_TYPE.EXPENSE }, { move_document_type: MOVE_DOC_TYPE.GBL }]; - expect(calcNetWeight(documents)).toBe(0); - }); - it('should return the net weight for weight ticket sets', () => { - const documents = [ - { move_document_type: MOVE_DOC_TYPE.WEIGHT_TICKET_SET, full_weight: 2000, empty_weight: 1000 }, - { move_document_type: MOVE_DOC_TYPE.WEIGHT_TICKET_SET, full_weight: 3000, empty_weight: 2000 }, - ]; - expect(calcNetWeight(documents)).toBe(2000); - }); - }); -}); diff --git a/src/scenes/MyMove/index.jsx b/src/scenes/MyMove/index.jsx index 60ef28fe14a..dcebf648f5c 100644 --- a/src/scenes/MyMove/index.jsx +++ b/src/scenes/MyMove/index.jsx @@ -40,16 +40,9 @@ import InfectedUpload from 'shared/Uploader/InfectedUpload'; import ProcessingUpload from 'shared/Uploader/ProcessingUpload'; import Edit from 'scenes/Review/Edit'; import EditProfile from 'scenes/Review/EditProfile'; -import WeightTicket from 'scenes/Moves/Ppm/WeightTicket'; -import ExpensesLanding from 'scenes/Moves/Ppm/ExpensesLanding'; -import ExpensesUpload from 'scenes/Moves/Ppm/ExpensesUpload'; -import AllowableExpenses from 'scenes/Moves/Ppm/AllowableExpenses'; -import WeightTicketExamples from 'scenes/Moves/Ppm/WeightTicketExamples'; import NotFound from 'components/NotFound/NotFound'; import PrivacyPolicyStatement from 'components/Statements/PrivacyAndPolicyStatement'; import AccessibilityStatement from 'components/Statements/AccessibilityStatement'; -import TrailerCriteria from 'scenes/Moves/Ppm/TrailerCriteria'; -import CustomerAgreementLegalese from 'scenes/Moves/Ppm/CustomerAgreementLegalese'; import ConnectedCreateOrEditMtoShipment from 'pages/MyMove/CreateOrEditMtoShipment'; import Home from 'pages/MyMove/Home'; import TitleAnnouncer from 'components/TitleAnnouncer/TitleAnnouncer'; @@ -215,17 +208,10 @@ export class CustomerApp extends Component { } /> } /> } /> - } /> - } /> - } /> } /> } /> - } /> - } /> - } /> } /> } /> - } /> {/* Errors */} { - const moveDocFieldProps = { - values: moveDocument, - schema: moveDocSchema, - }; - const isWeightTicketTypeCarOrTrailer = - isWeightTicketDocument && - (moveDocument.weight_ticket_set_type === WEIGHT_TICKET_SET_TYPE.CAR || - moveDocument.weight_ticket_set_type === WEIGHT_TICKET_SET_TYPE.CAR_TRAILER); - const isWeightTicketTypeBoxTruck = - isWeightTicketDocument && moveDocument.weight_ticket_set_type === WEIGHT_TICKET_SET_TYPE.BOX_TRUCK; - const isWeightTicketTypeProGear = - isWeightTicketDocument && moveDocument.weight_ticket_set_type === WEIGHT_TICKET_SET_TYPE.PRO_GEAR; - return ( -
-

- {renderStatusIcon(moveDocument.status)} - {moveDocument.title} -

-

- Uploaded {formatDate(get(moveDocument, 'document.uploads.0.createdAt'))} -

- - - {isExpenseDocument && moveDocument.moving_expense_type && ( - - )} - {isExpenseDocument && get(moveDocument, 'requested_amount_cents') && ( - - )} - {isExpenseDocument && get(moveDocument, 'payment_method') && ( - - )} - {isWeightTicketDocument && ( - <> - - - {isWeightTicketTypeBoxTruck && ( - - )} - {isWeightTicketTypeProGear && ( - - )} - {isWeightTicketTypeCarOrTrailer && ( - <> - - - - )} - - - - )} - {isStorageExpenseDocument && ( - <> - - - - )} - - -
- ); -}; - -const { bool, object, shape, string, arrayOf } = PropTypes; - -DocumentDetailDisplay.propTypes = { - isExpenseDocument: bool.isRequired, - isWeightTicketDocument: bool.isRequired, - moveDocSchema: shape({ - properties: object.isRequired, - required: arrayOf(string).isRequired, - type: string.isRequired, - }).isRequired, - moveDocument: shape({ - document: shape({ - id: string.isRequired, - service_member_id: string.isRequired, - uploads: ExistingUploadsShape.isRequired, - }), - id: string.isRequired, - move_document_type: string.isRequired, - move_id: string.isRequired, - notes: string, - personally_procured_move_id: string, - status: string.isRequired, - title: string.isRequired, - }).isRequired, -}; - -export default DocumentDetailDisplay; diff --git a/src/scenes/Office/DocumentViewer/DocumentDetailDisplay.test.jsx b/src/scenes/Office/DocumentViewer/DocumentDetailDisplay.test.jsx deleted file mode 100644 index c835d55eef9..00000000000 --- a/src/scenes/Office/DocumentViewer/DocumentDetailDisplay.test.jsx +++ /dev/null @@ -1,322 +0,0 @@ -import { shallow } from 'enzyme'; -import React from 'react'; - -import { MOVE_DOC_TYPE, WEIGHT_TICKET_SET_TYPE } from '../../../shared/constants'; - -import DocumentDetailDisplay from './DocumentDetailDisplay'; - -describe('DocumentDetailDisplay', () => { - const renderDocumentDetailDisplay = ({ - isExpenseDocument = false, - isWeightTicketDocument = true, - moveDocument = {}, - moveDocSchema = {}, - isStorageExpenseDocument = false, - }) => - shallow( - , - ); - - describe('weight ticket document display view', () => { - const requiredMoveDocumentFields = { - id: 'id', - move_id: 'id', - move_document_type: MOVE_DOC_TYPE.WEIGHT_TICKET_SET, - document: { - id: 'an id', - move_document_id: 'another id', - service_member_id: 'another id2', - uploads: [ - { - id: 'id', - url: 'url', - filename: 'file here', - contentType: 'json', - createdAt: '2018-09-27 08:14:38.702434', - }, - ], - }, - }; - it('includes common document info', () => { - const moveDocument = Object.assign(requiredMoveDocumentFields, { - title: 'My Title', - notes: 'This is a note', - status: 'AWAITING_REVIEW', - }); - - const moveDocSchema = { - properties: { - title: { enum: false }, - move_document_type: { enum: false }, - status: { enum: false }, - notes: { enum: false }, - }, - required: [], - type: 'string type', - }; - - const documentDisplay = renderDocumentDetailDisplay({ moveDocument, moveDocSchema }); - expect(documentDisplay.find('[data-testid="panel-subhead"]').text()).toContain(moveDocument.title); - expect(documentDisplay.find('[data-testid="uploaded-at"]').text()).toEqual('Uploaded 27-Sep-18'); - expect( - documentDisplay.find('[data-testid="document-title"]').dive().dive().find('SwaggerValue').dive().text(), - ).toEqual(moveDocument.title); - expect( - documentDisplay.find('[data-testid="move-document-type"]').dive().dive().find('SwaggerValue').dive().text(), - ).toEqual(moveDocument.move_document_type); - expect(documentDisplay.find('[data-testid="status"]').dive().dive().find('SwaggerValue').dive().text()).toEqual( - moveDocument.status, - ); - expect(documentDisplay.find('[data-testid="notes"]').dive().dive().find('SwaggerValue').dive().text()).toEqual( - moveDocument.notes, - ); - }); - - it('includes weight ticket-specific fields', () => { - const documentFieldsToTest = { - empty_weight: '2200', - full_weight: '3500', - }; - - const moveDocSchema = { - properties: { - empty_weight: { enum: false }, - full_weight: { enum: false }, - }, - required: [], - type: 'string type', - }; - - const moveDocument = Object.assign(requiredMoveDocumentFields, documentFieldsToTest); - const documentDisplay = renderDocumentDetailDisplay({ moveDocument, moveDocSchema }); - - expect( - documentDisplay.find('[data-testid="empty-weight"]').dive().dive().find('SwaggerValue').dive().text(), - ).toEqual(moveDocument.empty_weight); - expect( - documentDisplay.find('[data-testid="full-weight"]').dive().dive().find('SwaggerValue').dive().text(), - ).toEqual(moveDocument.full_weight); - }); - - describe('is car or car and trailer', () => { - it('includes the make and model fields ', () => { - const documentFieldsToTest = { - vehicle_make: 'Honda', - vehicle_model: 'Civic', - weight_ticket_set_type: WEIGHT_TICKET_SET_TYPE.CAR, - }; - - const moveDocSchema = { - properties: { - weight_ticket_set_type: { enum: false }, - vehicle_make: { enum: false }, - vehicle_model: { enum: false }, - }, - required: [], - type: 'string type', - }; - - const moveDocument = Object.assign(requiredMoveDocumentFields, documentFieldsToTest); - const documentDisplay = renderDocumentDetailDisplay({ moveDocument, moveDocSchema }); - expect( - documentDisplay - .find('[data-testid="weight-ticket-set-type"]') - .dive() - .dive() - .find('SwaggerValue') - .dive() - .text(), - ).toEqual(moveDocument.weight_ticket_set_type); - expect( - documentDisplay.find('[data-testid="vehicle-make"]').dive().dive().find('SwaggerValue').dive().text(), - ).toEqual(moveDocument.vehicle_make); - expect( - documentDisplay.find('[data-testid="vehicle-model"]').dive().dive().find('SwaggerValue').dive().text(), - ).toEqual(moveDocument.vehicle_model); - }); - }); - - describe('a box truck type weight ticket', () => { - it('includes vehicle nickname', () => { - const documentFieldsToTest = { - vehicle_nickname: '15 foot box truck', - weight_ticket_set_type: WEIGHT_TICKET_SET_TYPE.BOX_TRUCK, - }; - - const moveDocSchema = { - properties: { - weight_ticket_set_type: { enum: false }, - vehicle_nickname: { enum: false }, - }, - required: [], - type: 'string type', - }; - - const moveDocument = Object.assign(requiredMoveDocumentFields, documentFieldsToTest); - const documentDisplay = renderDocumentDetailDisplay({ moveDocument, moveDocSchema }); - expect( - documentDisplay - .find('[data-testid="weight-ticket-set-type"]') - .dive() - .dive() - .find('SwaggerValue') - .dive() - .text(), - ).toEqual(moveDocument.weight_ticket_set_type); - expect( - documentDisplay.find('[data-testid="vehicle-nickname"]').dive().dive().find('SwaggerValue').dive().text(), - ).toEqual(moveDocument.vehicle_nickname); - }); - }); - }); - describe('expense document display view', () => { - const requiredMoveDocumentFields = { - id: 'id', - move_id: 'id', - move_document_type: MOVE_DOC_TYPE.EXPENSE, - document: { - id: 'an id', - move_document_id: 'another id', - service_member_id: 'another id2', - uploads: [ - { - id: 'id', - url: 'url', - filename: 'file here', - contentType: 'json', - createdAt: '2018-09-27 08:14:38.702434', - }, - ], - }, - }; - it('has all expected fields', () => { - const moveDocument = Object.assign(requiredMoveDocumentFields, { - title: 'My Title', - move_document_type: MOVE_DOC_TYPE.EXPENSE, - moving_expense_type: 'RENTAL_EQUIPMENT', - requested_amount_cents: '45000', - payment_method: 'GCCC', - notes: 'This is a note', - status: 'AWAITING_REVIEW', - }); - - const moveDocSchema = { - properties: { - title: { enum: false }, - move_document_type: { enum: false }, - moving_expense_type: { enum: false }, - requested_amount_cents: { enum: false }, - payment_method: { enum: false }, - status: { enum: false }, - notes: { enum: false }, - }, - required: [], - type: 'string type', - }; - - const documentDisplay = renderDocumentDetailDisplay({ - isExpenseDocument: true, - isWeightTicketDocument: false, - moveDocument, - moveDocSchema, - }); - expect( - documentDisplay.find('[data-testid="document-title"]').dive().dive().find('SwaggerValue').dive().text(), - ).toEqual(moveDocument.title); - expect( - documentDisplay.find('[data-testid="move-document-type"]').dive().dive().find('SwaggerValue').dive().text(), - ).toEqual(moveDocument.move_document_type); - expect( - documentDisplay.find('[data-testid="moving-expense-type"]').dive().dive().find('SwaggerValue').dive().text(), - ).toEqual(moveDocument.moving_expense_type); - expect( - documentDisplay.find('[data-testid="requested-amount-cents"]').dive().dive().find('SwaggerValue').dive().text(), - ).toEqual(moveDocument.requested_amount_cents); - expect( - documentDisplay.find('[data-testid="payment-method"]').dive().dive().find('SwaggerValue').dive().text(), - ).toEqual(moveDocument.payment_method); - expect(documentDisplay.find('[data-testid="status"]').dive().dive().find('SwaggerValue').dive().text()).toEqual( - moveDocument.status, - ); - expect(documentDisplay.find('[data-testid="notes"]').dive().dive().find('SwaggerValue').dive().text()).toEqual( - moveDocument.notes, - ); - }); - }); - describe('storage expense document display view', () => { - const requiredMoveDocumentFields = { - id: 'id', - move_id: 'id', - move_document_type: MOVE_DOC_TYPE.EXPENSE, - document: { - id: 'an id', - move_document_id: 'another id', - service_member_id: 'another id2', - uploads: [ - { - id: 'id', - url: 'url', - filename: 'file here', - contentType: 'json', - createdAt: '2018-09-27 08:14:38.702434', - }, - ], - }, - }; - it('has all expected fields', () => { - const moveDocument = Object.assign(requiredMoveDocumentFields, { - title: 'My Title', - move_document_type: 'STORAGE_EXPENSE', - storage_start_date: '2018-09-27 08:14:38.702434', - storage_end_date: '2018-10-25 08:14:38.702434', - notes: 'This is a note', - status: 'AWAITING_REVIEW', - }); - - const moveDocSchema = { - properties: { - title: { enum: false }, - move_document_type: { enum: false }, - storage_start_date: { enum: false }, - storage_end_date: { enum: false }, - status: { enum: false }, - notes: { enum: false }, - }, - required: [], - type: 'string type', - }; - - const documentDisplay = renderDocumentDetailDisplay({ - isExpenseDocument: false, - isWeightTicketDocument: false, - isStorageExpenseDocument: true, - moveDocument, - moveDocSchema, - }); - expect( - documentDisplay.find('[data-testid="document-title"]').dive().dive().find('SwaggerValue').dive().text(), - ).toEqual(moveDocument.title); - expect( - documentDisplay.find('[data-testid="move-document-type"]').dive().dive().find('SwaggerValue').dive().text(), - ).toEqual(moveDocument.move_document_type); - expect( - documentDisplay.find('[data-testid="storage-start-date"]').dive().dive().find('SwaggerValue').dive().text(), - ).toEqual(moveDocument.storage_start_date); - expect( - documentDisplay.find('[data-testid="storage-end-date"]').dive().dive().find('SwaggerValue').dive().text(), - ).toEqual(moveDocument.storage_end_date); - expect(documentDisplay.find('[data-testid="status"]').dive().dive().find('SwaggerValue').dive().text()).toEqual( - moveDocument.status, - ); - expect(documentDisplay.find('[data-testid="notes"]').dive().dive().find('SwaggerValue').dive().text()).toEqual( - moveDocument.notes, - ); - }); - }); -}); diff --git a/src/scenes/Office/DocumentViewer/DocumentDetailEdit.jsx b/src/scenes/Office/DocumentViewer/DocumentDetailEdit.jsx deleted file mode 100644 index 6aa4a75aca4..00000000000 --- a/src/scenes/Office/DocumentViewer/DocumentDetailEdit.jsx +++ /dev/null @@ -1,132 +0,0 @@ -import React, { Fragment } from 'react'; -import PropTypes from 'prop-types'; -import { get, isEmpty } from 'lodash'; -import { FormSection } from 'redux-form'; - -import { SwaggerField } from 'shared/JsonSchemaForm/JsonSchemaField'; -import { MOVE_DOC_TYPE, WEIGHT_TICKET_SET_TYPE } from 'shared/constants'; -import ExpenseDocumentForm from 'scenes/Office/DocumentViewer/ExpenseDocumentForm'; -import LoadingPlaceholder from 'shared/LoadingPlaceholder'; - -const DocumentDetailEdit = ({ formValues, moveDocSchema }) => { - const isExpenseDocument = get(formValues.moveDocument, 'move_document_type') === MOVE_DOC_TYPE.EXPENSE; - const isWeightTicketDocument = get(formValues.moveDocument, 'move_document_type') === MOVE_DOC_TYPE.WEIGHT_TICKET_SET; - const isStorageExpenseDocument = - get(formValues.moveDocument, 'move_document_type') === 'EXPENSE' && - get(formValues.moveDocument, 'moving_expense_type') === 'STORAGE'; - const isWeightTicketTypeCarOrTrailer = - isWeightTicketDocument && - (formValues.moveDocument.weight_ticket_set_type === WEIGHT_TICKET_SET_TYPE.CAR || - formValues.moveDocument.weight_ticket_set_type === WEIGHT_TICKET_SET_TYPE.CAR_TRAILER); - const isWeightTicketTypeBoxTruck = - isWeightTicketDocument && formValues.moveDocument.weight_ticket_set_type === WEIGHT_TICKET_SET_TYPE.BOX_TRUCK; - const isWeightTicketTypeProGear = - isWeightTicketDocument && formValues.moveDocument.weight_ticket_set_type === WEIGHT_TICKET_SET_TYPE.PRO_GEAR; - - return isEmpty(formValues.moveDocument) ? ( - - ) : ( -
- - - - {isExpenseDocument && } - {isWeightTicketDocument && ( - <> -
- -
- {isWeightTicketTypeBoxTruck && ( - - )} - {isWeightTicketTypeProGear && ( - - )} - {isWeightTicketTypeCarOrTrailer && ( - <> - - - - )} - {' '} - lbs - {' '} - lbs - - )} - {isStorageExpenseDocument && ( - <> - - - - )} - - -
-
- ); -}; -const { object, shape, string, arrayOf } = PropTypes; - -DocumentDetailEdit.propTypes = { - moveDocSchema: shape({ - properties: object.isRequired, - required: arrayOf(string).isRequired, - type: string.isRequired, - }).isRequired, -}; - -export default DocumentDetailEdit; diff --git a/src/scenes/Office/DocumentViewer/DocumentDetailEdit.test.jsx b/src/scenes/Office/DocumentViewer/DocumentDetailEdit.test.jsx deleted file mode 100644 index 0a33d8b58d0..00000000000 --- a/src/scenes/Office/DocumentViewer/DocumentDetailEdit.test.jsx +++ /dev/null @@ -1,167 +0,0 @@ -import { shallow } from 'enzyme'; -import React from 'react'; - -import { MOVE_DOC_TYPE, WEIGHT_TICKET_SET_TYPE } from '../../../shared/constants'; - -import DocumentDetailEdit from './DocumentDetailEdit'; - -describe('DocumentDetailEdit', () => { - const renderDocumentDetailEdit = ({ moveDocSchema = {}, formValues = { moveDocument: {} } }) => - shallow(); - - const moveDocSchema = { - properties: {}, - required: [], - type: 'string type', - }; - - describe('weight ticket document edit', () => { - it('shows all form fields for a car', () => { - const formValues = { - moveDocument: { - move_document_type: MOVE_DOC_TYPE.WEIGHT_TICKET_SET, - weight_ticket_set_type: WEIGHT_TICKET_SET_TYPE.CAR, - }, - }; - - const documentForm = renderDocumentDetailEdit({ formValues, moveDocSchema }); - - const title = documentForm.find('[data-testid="title"]'); - const moveDocumentType = documentForm.find('[data-testid="move-document-type"]'); - const weightTicketSetType = documentForm.find('[data-testid="weight-ticket-set-type"]'); - const make = documentForm.find('[data-testid="vehicle-make"]'); - const model = documentForm.find('[data-testid="vehicle-model"]'); - const vehicleNickname = documentForm.find('[data-testid="vehicle-nickname"]'); - const status = documentForm.find('[data-testid="status"]'); - const notes = documentForm.find('[data-testid="notes"]'); - - expect(title.props()).toHaveProperty('fieldName', 'title'); - expect(moveDocumentType.props()).toHaveProperty('fieldName', 'move_document_type'); - expect(weightTicketSetType.props()).toHaveProperty('fieldName', 'weight_ticket_set_type'); - expect(make.props()).toHaveProperty('fieldName', 'vehicle_make'); - expect(model.props()).toHaveProperty('fieldName', 'vehicle_model'); - expect(vehicleNickname.length).toBeFalsy(); - expect(status.props()).toHaveProperty('fieldName', 'status'); - expect(notes.props()).toHaveProperty('fieldName', 'notes'); - }); - - it('shows all form fields for a car+trailer', () => { - const formValues = { - moveDocument: { - move_document_type: MOVE_DOC_TYPE.WEIGHT_TICKET_SET, - weight_ticket_set_type: WEIGHT_TICKET_SET_TYPE.CAR_TRAILER, - }, - }; - - const documentForm = renderDocumentDetailEdit({ formValues, moveDocSchema }); - - const title = documentForm.find('[data-testid="title"]'); - const moveDocumentType = documentForm.find('[data-testid="move-document-type"]'); - const weightTicketSetType = documentForm.find('[data-testid="weight-ticket-set-type"]'); - const make = documentForm.find('[data-testid="vehicle-make"]'); - const model = documentForm.find('[data-testid="vehicle-model"]'); - const vehicleNickname = documentForm.find('[data-testid="vehicle-nickname"]'); - const status = documentForm.find('[data-testid="status"]'); - const notes = documentForm.find('[data-testid="notes"]'); - - expect(title.props()).toHaveProperty('fieldName', 'title'); - expect(moveDocumentType.props()).toHaveProperty('fieldName', 'move_document_type'); - expect(weightTicketSetType.props()).toHaveProperty('fieldName', 'weight_ticket_set_type'); - expect(make.props()).toHaveProperty('fieldName', 'vehicle_make'); - expect(model.props()).toHaveProperty('fieldName', 'vehicle_model'); - expect(vehicleNickname.length).toBeFalsy(); - expect(status.props()).toHaveProperty('fieldName', 'status'); - expect(notes.props()).toHaveProperty('fieldName', 'notes'); - }); - - it('shows all form fields for a boxtruck', () => { - const formValues = { - moveDocument: { - move_document_type: MOVE_DOC_TYPE.WEIGHT_TICKET_SET, - weight_ticket_set_type: WEIGHT_TICKET_SET_TYPE.BOX_TRUCK, - }, - }; - - const documentForm = renderDocumentDetailEdit({ formValues, moveDocSchema }); - const title = documentForm.find('[data-testid="title"]'); - const moveDocumentType = documentForm.find('[data-testid="move-document-type"]'); - const weightTicketSetType = documentForm.find('[data-testid="weight-ticket-set-type"]'); - const make = documentForm.find('[data-testid="vehicle-make"]'); - const model = documentForm.find('[data-testid="vehicle-model"]'); - const vehicleNickname = documentForm.find('[data-testid="vehicle-nickname"]'); - const emptyWeight = documentForm.find('[data-testid="empty-weight"]'); - const fullWeight = documentForm.find('[data-testid="full-weight"]'); - const status = documentForm.find('[data-testid="status"]'); - const notes = documentForm.find('[data-testid="notes"]'); - - expect(title.props()).toHaveProperty('fieldName', 'title'); - expect(moveDocumentType.props()).toHaveProperty('fieldName', 'move_document_type'); - expect(weightTicketSetType.props()).toHaveProperty('fieldName', 'weight_ticket_set_type'); - expect(make.length).toBeFalsy(); - expect(model.length).toBeFalsy(); - expect(vehicleNickname.props()).toHaveProperty('fieldName', 'vehicle_nickname'); - expect(emptyWeight.props()).toHaveProperty('fieldName', 'empty_weight'); - expect(fullWeight.props()).toHaveProperty('fieldName', 'full_weight'); - expect(status.props()).toHaveProperty('fieldName', 'status'); - expect(notes.props()).toHaveProperty('fieldName', 'notes'); - }); - it('shows all form fields for progear', () => { - const formValues = { - moveDocument: { - move_document_type: MOVE_DOC_TYPE.WEIGHT_TICKET_SET, - weight_ticket_set_type: WEIGHT_TICKET_SET_TYPE.PRO_GEAR, - }, - }; - - const documentForm = renderDocumentDetailEdit({ formValues, moveDocSchema }); - const title = documentForm.find('[data-testid="title"]'); - const moveDocumentType = documentForm.find('[data-testid="move-document-type"]'); - const weightTicketSetType = documentForm.find('[data-testid="weight-ticket-set-type"]'); - const make = documentForm.find('[data-testid="vehicle-make"]'); - const model = documentForm.find('[data-testid="vehicle-model"]'); - const progearType = documentForm.find('[data-testid="progear-type"]'); - const emptyWeight = documentForm.find('[data-testid="empty-weight"]'); - const fullWeight = documentForm.find('[data-testid="full-weight"]'); - const status = documentForm.find('[data-testid="status"]'); - const notes = documentForm.find('[data-testid="notes"]'); - - expect(title.props()).toHaveProperty('fieldName', 'title'); - expect(moveDocumentType.props()).toHaveProperty('fieldName', 'move_document_type'); - expect(weightTicketSetType.props()).toHaveProperty('fieldName', 'weight_ticket_set_type'); - expect(make.length).toBeFalsy(); - expect(model.length).toBeFalsy(); - expect(progearType.props()).toHaveProperty('fieldName', 'vehicle_nickname'); - expect(emptyWeight.props()).toHaveProperty('fieldName', 'empty_weight'); - expect(fullWeight.props()).toHaveProperty('fieldName', 'full_weight'); - expect(status.props()).toHaveProperty('fieldName', 'status'); - expect(notes.props()).toHaveProperty('fieldName', 'notes'); - }); - }); - describe('expense document type', () => { - it('shows all form fields for storage expense document type', () => { - const formValues = { - moveDocument: { - move_document_type: MOVE_DOC_TYPE.EXPENSE, - moving_expense_type: 'STORAGE', - }, - }; - - const documentForm = renderDocumentDetailEdit({ formValues, moveDocSchema }); - const title = documentForm.find('[data-testid="title"]'); - const moveDocumentType = documentForm.find('[data-testid="move-document-type"]'); - const storageStartDate = documentForm.find('[data-testid="storage-start-date"]'); - const storageEndDate = documentForm.find('[data-testid="storage-end-date"]'); - const status = documentForm.find('[data-testid="status"]'); - const notes = documentForm.find('[data-testid="notes"]'); - const expenseDocumentForm = documentForm.find('ExpenseDocumentForm'); - - expect(title.props()).toHaveProperty('fieldName', 'title'); - expect(moveDocumentType.props()).toHaveProperty('fieldName', 'move_document_type'); - expect(storageStartDate.props()).toHaveProperty('fieldName', 'storage_start_date'); - expect(storageEndDate.props()).toHaveProperty('fieldName', 'storage_end_date'); - expect(status.props()).toHaveProperty('fieldName', 'status'); - expect(notes.props()).toHaveProperty('fieldName', 'notes'); - expect(expenseDocumentForm.props()).toBeTruthy(); - }); - }); -}); diff --git a/src/scenes/Office/DocumentViewer/DocumentDetailPanel.jsx b/src/scenes/Office/DocumentViewer/DocumentDetailPanel.jsx deleted file mode 100644 index 0a781dfe4cd..00000000000 --- a/src/scenes/Office/DocumentViewer/DocumentDetailPanel.jsx +++ /dev/null @@ -1,78 +0,0 @@ -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { get, omit, cloneDeep } from 'lodash'; -import { reduxForm, getFormValues } from 'redux-form'; - -import DocumentDetailDisplay from './DocumentDetailDisplay'; -import DocumentDetailEdit from './DocumentDetailEdit'; - -import { convertDollarsToCents } from 'shared/utils'; -import { formatCents } from 'utils/formatters'; -import { editablePanelify } from 'shared/EditablePanel'; -import { selectMoveDocument, updateMoveDocument } from 'shared/Entities/modules/moveDocuments'; -import { selectActivePPMForMove } from 'shared/Entities/modules/ppms'; -import { isMovingExpenseDocument } from 'shared/Entities/modules/movingExpenseDocuments'; - -const formName = 'move_document_viewer'; - -let DocumentDetailPanel = editablePanelify(DocumentDetailDisplay, DocumentDetailEdit); - -DocumentDetailPanel = reduxForm({ form: formName })(DocumentDetailPanel); - -function mapStateToProps(state, props) { - const { moveId, moveDocumentId } = props; - const moveDocument = selectMoveDocument(state, moveDocumentId); - const isExpenseDocument = isMovingExpenseDocument(moveDocument); - const isWeightTicketDocument = get(moveDocument, 'move_document_type') === 'WEIGHT_TICKET_SET'; - const isStorageExpenseDocument = - get(moveDocument, 'move_document_type') === 'EXPENSE' && get(moveDocument, 'moving_expense_type') === 'STORAGE'; - // Convert cents to collars - make a deep clone copy to not modify moveDocument itself - const initialMoveDocument = cloneDeep(moveDocument); - const requested_amount = get(initialMoveDocument, 'requested_amount_cents'); - if (requested_amount) { - initialMoveDocument.requested_amount_cents = formatCents(requested_amount); - } - - return { - // reduxForm - initialValues: { - moveDocument: initialMoveDocument, - }, - isExpenseDocument, - isWeightTicketDocument, - isStorageExpenseDocument, - formValues: getFormValues(formName)(state), - moveDocSchema: get(state, 'swaggerInternal.spec.definitions.MoveDocumentPayload', {}), - hasError: false, - isUpdating: false, - moveDocument, - - // editablePanelify - getUpdateArgs() { - // Make a copy of values to not modify moveDocument - const values = cloneDeep(getFormValues(formName)(state)); - values.moveDocument.personally_procured_move_id = selectActivePPMForMove(state, props.moveId).id; - if ( - get(values.moveDocument, 'move_document_type', '') !== 'EXPENSE' && - get(values.moveDocument, 'payment_method', false) - ) { - values.moveDocument = omit(values.moveDocument, ['payment_method', 'requested_amount_cents']); - } - if (get(values.moveDocument, 'move_document_type', '') === 'EXPENSE') { - values.moveDocument.requested_amount_cents = convertDollarsToCents(values.moveDocument.requested_amount_cents); - } - return [moveId, moveDocumentId, values.moveDocument]; - }, - }; -} - -function mapDispatchToProps(dispatch) { - return bindActionCreators( - { - update: updateMoveDocument, - }, - dispatch, - ); -} - -export default connect(mapStateToProps, mapDispatchToProps)(DocumentDetailPanel); diff --git a/src/scenes/Office/DocumentViewer/ExpenseDocumentForm.jsx b/src/scenes/Office/DocumentViewer/ExpenseDocumentForm.jsx deleted file mode 100644 index 1e9882211b6..00000000000 --- a/src/scenes/Office/DocumentViewer/ExpenseDocumentForm.jsx +++ /dev/null @@ -1,19 +0,0 @@ -import React, { Fragment } from 'react'; -import PropTypes from 'prop-types'; - -import { SwaggerField } from 'shared/JsonSchemaForm/JsonSchemaField'; - -const ExpenseDocumentForm = (props) => { - const { moveDocSchema } = props; - return ( - <> - - - - - ); -}; -ExpenseDocumentForm.propTypes = { - moveDocSchema: PropTypes.object, -}; -export default ExpenseDocumentForm; diff --git a/src/scenes/Office/DocumentViewer/index.css b/src/scenes/Office/DocumentViewer/index.css deleted file mode 100644 index 98e36d22a9f..00000000000 --- a/src/scenes/Office/DocumentViewer/index.css +++ /dev/null @@ -1,61 +0,0 @@ -.doc-viewer .doc-viewer-tabs { - border-bottom: 1px solid #d7d7d7; - width: 100%; - margin-top: 2em; - position: relative; - left: -1.5em; - padding-left: 1.5em; -} - -.doc-viewer .nav-tab { - display: inline-block; - font-weight: bold; - color: #5b616b; - border-bottom: 6px solid #fff; - margin-right: 1.5em; - margin-bottom: 0; - cursor: pointer; - text-decoration: none; -} - -.doc-viewer .react-tabs__tab-list { - padding-left: 0; -} - -.doc-viewer .react-tabs__tab--selected { - color: black; - border-bottom-color: #0070bd; - cursor: auto; -} - -.doc-viewer .tab-content { - border-top: none; -} - -.doc-viewer .tab-content a { - text-decoration: none; - color: #0071bc; -} - -.pad-ns { - padding: 2rem 0rem 2rem 0rem; -} - -.document-contents { - background: lightgray; -} - -.document-contents .pdf-placeholder { - padding: 1em; - background: white; - font-style: italic; - text-align: right; -} - -.document-contents .pdf-placeholder .filename { - font-weight: bold; - font-style: normal; - float: left; -} - - diff --git a/src/scenes/Office/DocumentViewer/index.jsx b/src/scenes/Office/DocumentViewer/index.jsx deleted file mode 100644 index 3d72175edf6..00000000000 --- a/src/scenes/Office/DocumentViewer/index.jsx +++ /dev/null @@ -1,220 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { includes, get, isEmpty } from 'lodash'; -import qs from 'query-string'; -import { Tab, Tabs, TabList, TabPanel } from 'react-tabs'; - -import DocumentDetailPanel from './DocumentDetailPanel'; - -import { selectMove, loadMove, loadMoveLabel } from 'shared/Entities/modules/moves'; -import { createMovingExpenseDocument } from 'shared/Entities/modules/movingExpenseDocuments'; -import LoadingPlaceholder from 'shared/LoadingPlaceholder'; -import Alert from 'shared/Alert'; -import { PanelField } from 'shared/EditablePanel'; -import { getRequestStatus } from 'shared/Swagger/selectors'; -import { loadServiceMember, selectServiceMember } from 'shared/Entities/modules/serviceMembers'; -import DocumentList from 'shared/DocumentViewer/DocumentList'; -import { selectActivePPMForMove } from 'shared/Entities/modules/ppms'; -import { - selectAllDocumentsForMove, - getMoveDocumentsForMove, - createMoveDocument, -} from 'shared/Entities/modules/moveDocuments'; -import { stringifyName } from 'shared/utils/serviceMember'; -import { convertDollarsToCents } from 'shared/utils'; -import { generatePageTitle } from 'hooks/custom'; - -import './index.css'; -import { RouterShape } from 'types'; -import withRouter from 'utils/routing'; - -class DocumentViewer extends Component { - componentDidMount() { - const { moveId } = this.props; - this.props.loadMove(moveId); - this.props.getMoveDocumentsForMove(moveId); - } - - componentDidUpdate(prevProps) { - const { serviceMemberId } = this.props; - if (serviceMemberId !== prevProps.serviceMemberId) { - this.props.loadServiceMember(serviceMemberId); - } - } - - get getDocumentUploaderProps() { - const { - docTypes, - router: { location }, - genericMoveDocSchema, - moveDocSchema, - } = this.props; - // Parse query string parameters - const { moveDocumentType } = qs.parse(location.search); - - const initialValues = {}; - // Verify the provided doc type against the schema - if (includes(docTypes, moveDocumentType)) { - initialValues.move_document_type = moveDocumentType; - } - - return { - form: 'move_document_upload', - isPublic: false, - onSubmit: this.handleSubmit, - genericMoveDocSchema, - initialValues, - moveDocSchema, - }; - } - - handleSubmit = (uploadIds, formValues) => { - const { currentPpm, moveId } = this.props; - const { - title, - moving_expense_type: movingExpenseType, - move_document_type: moveDocumentType, - requested_amount_cents: requestedAmountCents, - payment_method: paymentMethod, - notes, - } = formValues; - const personallyProcuredMoveId = currentPpm ? currentPpm.id : null; - if (get(formValues, 'move_document_type', false) === 'EXPENSE') { - return this.props.createMovingExpenseDocument({ - moveId, - personallyProcuredMoveId, - uploadIds, - title, - movingExpenseType, - moveDocumentType, - requestedAmountCents: convertDollarsToCents(requestedAmountCents), - paymentMethod, - notes, - missingReceipt: false, - }); - } - return this.props.createMoveDocument({ - moveId, - personallyProcuredMoveId, - uploadIds, - title, - moveDocumentType, - notes, - }); - }; - - render() { - const { serviceMember, moveId, moveDocumentId, moveDocuments, moveLocator } = this.props; - const numMoveDocs = moveDocuments ? moveDocuments.length : 0; - const name = stringifyName(serviceMember); - document.title = generatePageTitle(`Document Viewer for ${name}`); - - // urls: has full url with IDs - const newUrl = `/moves/${moveId}/documents/new`; - - const defaultTabIndex = moveDocumentId !== 'new' ? 1 : 0; - - if (!this.props.loadDependenciesHasSuccess && !this.props.loadDependenciesHasError) return ; - if (this.props.loadDependenciesHasError) - return ( -
-
-
- - Something went wrong contacting the server. - -
-
-
- ); - return ( -
-
-
-

{name}

- {moveLocator} - {serviceMember.edipi} -
- - - All Documents ({numMoveDocs}) - {/* TODO: Handle routing of /new route better */} - {moveDocumentId && moveDocumentId !== 'new' && Details} - - - -
- {' '} - -
-
- - {!isEmpty(moveDocuments) && moveDocumentId && moveDocumentId !== 'new' && ( - - - - )} -
-
-
-
-
- ); - } -} - -DocumentViewer.propTypes = { - docTypes: PropTypes.arrayOf(PropTypes.string).isRequired, - loadMove: PropTypes.func.isRequired, - getMoveDocumentsForMove: PropTypes.func.isRequired, - genericMoveDocSchema: PropTypes.object.isRequired, - moveDocSchema: PropTypes.object.isRequired, - moveDocuments: PropTypes.arrayOf(PropTypes.object), - router: RouterShape.isRequired(), -}; - -const mapStateToProps = (state, { router: { params } }) => { - const { moveId, moveDocumentId } = params; - const move = selectMove(state, moveId); - const moveLocator = move.locator; - const serviceMemberId = move.service_member_id; - const serviceMember = selectServiceMember(state, serviceMemberId); - const loadMoveRequest = getRequestStatus(state, loadMoveLabel); - - return { - genericMoveDocSchema: get(state, 'swaggerInternal.spec.definitions.CreateGenericMoveDocumentPayload', {}), - moveDocSchema: get(state, 'swaggerInternal.spec.definitions.MoveDocumentPayload', {}), - currentPpm: selectActivePPMForMove(state, moveId), - docTypes: get(state, 'swaggerInternal.spec.definitions.MoveDocumentType.enum', []), - moveId, - moveLocator, - moveDocumentId, - moveDocuments: selectAllDocumentsForMove(state, moveId), - serviceMember, - serviceMemberId, - loadDependenciesHasSuccess: loadMoveRequest.isSuccess, - loadDependenciesHasError: loadMoveRequest.error, - }; -}; - -const mapDispatchToProps = { - createMoveDocument, - createMovingExpenseDocument, - loadMove, - loadServiceMember, - getMoveDocumentsForMove, -}; - -export default withRouter(connect(mapStateToProps, mapDispatchToProps)(DocumentViewer)); diff --git a/src/scenes/Office/Ppm/DatesAndLocationsPanel.jsx b/src/scenes/Office/Ppm/DatesAndLocationsPanel.jsx deleted file mode 100644 index 2b17b6823e7..00000000000 --- a/src/scenes/Office/Ppm/DatesAndLocationsPanel.jsx +++ /dev/null @@ -1,94 +0,0 @@ -import { get } from 'lodash'; -import React from 'react'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { reduxForm, getFormValues } from 'redux-form'; - -import { editablePanelify, PanelSwaggerField } from 'shared/EditablePanel'; -import { SwaggerField } from 'shared/JsonSchemaForm/JsonSchemaField'; -import { selectActivePPMForMove, updatePPM } from 'shared/Entities/modules/ppms'; - -const DatesAndLocationDisplay = (props) => { - const fieldProps = { - schema: props.ppmSchema, - values: props.ppm, - }; - return ( -
- - - -
- ); -}; - -const DatesAndLocationEdit = (props) => { - const schema = props.ppmSchema; - return ( -
- - - -
- ); -}; - -const formName = 'ppm_dates_and_locations'; - -let DatesAndLocationPanel = editablePanelify(DatesAndLocationDisplay, DatesAndLocationEdit); -DatesAndLocationPanel = reduxForm({ - form: formName, - enableReinitialize: true, - keepDirtyOnReinitialize: true, -})(DatesAndLocationPanel); - -function mapStateToProps(state, props) { - const formValues = getFormValues(formName)(state); - const ppm = selectActivePPMForMove(state, props.moveId); - - return { - // reduxForm - formValues, - initialValues: { - actual_move_date: ppm.actual_move_date, - pickup_postal_code: ppm.pickup_postal_code, - destination_postal_code: ppm.destination_postal_code, - }, - - ppmSchema: get(state, 'swaggerInternal.spec.definitions.PersonallyProcuredMovePayload'), - ppm, - - hasError: !!props.error, - errorMessage: get(state, 'office.error'), - isUpdating: false, - - // editablePanelify - getUpdateArgs() { - const values = getFormValues(formName)(state); - return [props.moveId, ppm.id, values]; - }, - }; -} - -function mapDispatchToProps(dispatch) { - return bindActionCreators( - { - update: updatePPM, - }, - dispatch, - ); -} - -export default connect(mapStateToProps, mapDispatchToProps)(DatesAndLocationPanel); diff --git a/src/scenes/Office/Ppm/PPMEstimatesPanel.jsx b/src/scenes/Office/Ppm/PPMEstimatesPanel.jsx deleted file mode 100644 index e59af7a9238..00000000000 --- a/src/scenes/Office/Ppm/PPMEstimatesPanel.jsx +++ /dev/null @@ -1,149 +0,0 @@ -import { get } from 'lodash'; -import React from 'react'; -import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; -import { FormSection, getFormValues, reduxForm } from 'redux-form'; - -import { editablePanelify, PanelField, PanelSwaggerField } from 'shared/EditablePanel'; -import { formatCentsRange } from 'utils/formatters'; -import { SwaggerField } from 'shared/JsonSchemaForm/JsonSchemaField'; -import YesNoBoolean from 'shared/Inputs/YesNoBoolean'; -import { selectActivePPMForMove, updatePPM } from 'shared/Entities/modules/ppms'; -import { calculateEntitlementsForMove } from 'shared/Entities/modules/moves'; - -const validateWeight = (value, formValues, props, fieldName) => { - if (value && props.entitlement && value > props.entitlement.sum) { - return `Cannot be more than full entitlement weight (${props.entitlement.sum} lbs)`; - } - return undefined; -}; - -const EstimatesDisplay = (props) => { - const ppm = props.PPMEstimate; - const fieldProps = { - schema: props.ppmSchema, - values: ppm, - }; - - return ( - <> -
- - {formatCentsRange(ppm.incentive_estimate_min, ppm.incentive_estimate_max)} - - - {fieldProps.values.has_pro_gear && ( - - )} - {fieldProps.values.has_pro_gear_over_thousand && ( - - )} - - - {fieldProps.values.has_sit ? 'Yes' : 'No'} - - {fieldProps.values.has_sit && ( - - )} -
-
- - - -
- - ); -}; - -const EstimatesEdit = (props) => { - const ppm = props.PPMEstimate; - const schema = props.ppmSchema; - - return ( - -
- - {formatCentsRange(ppm.incentive_estimate_min, ppm.incentive_estimate_max)} - - {' '} - lbs - - - -
Storage
- - {get(props, 'formValues.PPMEstimate.has_sit', false) && ( - - )} -
-
- - - -
-
- ); -}; - -const formName = 'ppm_estimate_and_details'; - -let PPMEstimatesPanel = editablePanelify(EstimatesDisplay, EstimatesEdit); -PPMEstimatesPanel = reduxForm({ form: formName })(PPMEstimatesPanel); - -function mapStateToProps(state, ownProps) { - const PPMEstimate = selectActivePPMForMove(state, ownProps.moveId); - const formValues = getFormValues(formName)(state); - - return { - // reduxForm - formValues, - initialValues: { PPMEstimate }, - - // Wrapper - ppmSchema: get(state, 'swaggerInternal.spec.definitions.PersonallyProcuredMovePayload'), - hasError: false, - errorMessage: get(state, 'office.error'), - PPMEstimate, - isUpdating: false, - entitlement: calculateEntitlementsForMove(state, ownProps.moveId), - - // editablePanelify - getUpdateArgs() { - if ( - formValues.PPMEstimate.additional_pickup_postal_code !== '' && - formValues.PPMEstimate.additional_pickup_postal_code !== undefined - ) { - formValues.PPMEstimate.has_additional_postal_code = true; - } else { - delete formValues.PPMEstimate.additional_pickup_postal_code; - formValues.PPMEstimate.has_additional_postal_code = false; - } - return [ownProps.moveId, formValues.PPMEstimate.id, formValues.PPMEstimate]; - }, - }; -} - -function mapDispatchToProps(dispatch) { - return bindActionCreators( - { - update: updatePPM, - }, - dispatch, - ); -} - -export default connect(mapStateToProps, mapDispatchToProps)(PPMEstimatesPanel); diff --git a/src/scenes/Office/Ppm/StoragePanel.js b/src/scenes/Office/Ppm/StoragePanel.js deleted file mode 100644 index b9a4bea5a35..00000000000 --- a/src/scenes/Office/Ppm/StoragePanel.js +++ /dev/null @@ -1,126 +0,0 @@ -import { get, filter } from 'lodash'; -import React from 'react'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { reduxForm, getFormValues } from 'redux-form'; - -import { convertDollarsToCents } from '../../../shared/utils'; - -import { getDocsByStatusAndType } from './ducks'; - -import Alert from 'shared/Alert'; -import { editablePanelify, PanelSwaggerField } from 'shared/EditablePanel'; -import { SwaggerField } from 'shared/JsonSchemaForm/JsonSchemaField'; -import { selectActivePPMForMove, updatePPM } from 'shared/Entities/modules/ppms'; -import { formatCents } from 'utils/formatters'; - -const StorageDisplay = (props) => { - const cost = props.ppm && props.ppm.total_sit_cost ? formatCents(props.ppm.total_sit_cost) : 0; - const days = props.ppm && props.ppm.days_in_storage ? props.ppm.days_in_storage : 0; - - const fieldProps = { - schema: { - properties: { - days_in_storage: { - maximum: 90, - minimum: 0, - title: 'How many days do you plan to put your stuff in storage?', - type: 'integer', - 'x-nullable': true, - }, - total_sit_cost: { - type: 'string', - }, - }, - }, - values: { - total_sit_cost: `$${cost}`, - days_in_storage: `${days}`, - }, - }; - - return ( -
- {props.awaitingStorageExpenses.length > 0 && ( -
- There are more storage receipts awaiting review -
- )} - - -
- ); -}; - -const StorageEdit = (props) => { - const schema = props.ppmSchema; - - return ( -
- - -
- ); -}; - -const formName = 'ppm_sit_storage'; - -let StoragePanel = editablePanelify(StorageDisplay, StorageEdit); -StoragePanel = reduxForm({ - form: formName, - enableReinitialize: true, -})(StoragePanel); - -function mapStateToProps(state, props) { - const formValues = getFormValues(formName)(state); - const ppm = selectActivePPMForMove(state, props.moveId); - const storageExpenses = filter(props.moveDocuments, ['moving_expense_type', 'STORAGE']); - - return { - // reduxForm - formValues, - initialValues: { - total_sit_cost: formatCents(ppm.total_sit_cost), - days_in_storage: ppm.days_in_storage, - }, - - ppmSchema: get(state, 'swaggerInternal.spec.definitions.PersonallyProcuredMovePayload'), - ppm, - - hasError: !!props.error, - errorMessage: get(state, 'office.error'), - isUpdating: false, - awaitingStorageExpenses: getDocsByStatusAndType(storageExpenses, 'OK'), - - // editablePanelify - getUpdateArgs() { - const values = getFormValues(formName)(state); - const adjustedValues = { - total_sit_cost: convertDollarsToCents(values.total_sit_cost), - days_in_storage: values.days_in_storage, - }; - return [props.moveId, ppm.id, adjustedValues]; - }, - }; -} - -function mapDispatchToProps(dispatch) { - return bindActionCreators( - { - update: updatePPM, - }, - dispatch, - ); -} - -export default connect(mapStateToProps, mapDispatchToProps)(StoragePanel); diff --git a/src/scenes/Office/Ppm/StoragePanel.test.js b/src/scenes/Office/Ppm/StoragePanel.test.js deleted file mode 100644 index 7d33b7a89dc..00000000000 --- a/src/scenes/Office/Ppm/StoragePanel.test.js +++ /dev/null @@ -1,44 +0,0 @@ -import React from 'react'; -import { Provider } from 'react-redux'; -import { mount } from 'enzyme'; - -import StoragePanel from './StoragePanel'; - -import store from 'shared/store'; -import Alert from 'shared/Alert'; -import { PanelSwaggerField } from 'shared/EditablePanel'; - -describe('StoragePanel', () => { - describe('Receipts Awaiting Review Alert', () => { - it('is non-existent when all receipts have OK status', () => { - const moveDocuments = [ - { moving_expense_type: 'STORAGE', status: 'OK' }, - { moving_expense_type: 'STORAGE', status: 'OK' }, - ]; - const moveId = 'some ID'; - - const wrapper = mount( - - - , - ); - expect(wrapper.find(PanelSwaggerField)).toHaveLength(2); - expect(wrapper.find(Alert)).toHaveLength(0); - }); - it('is existent when any receipt does not have an have OK status', () => { - const moveDocuments = [ - { moving_expense_type: 'STORAGE', status: 'OK' }, - { moving_expense_type: 'STORAGE', status: 'HAS_ISSUE' }, - ]; - const moveId = 'some ID'; - - const wrapper = mount( - - - , - ); - expect(wrapper.find(PanelSwaggerField)).toHaveLength(2); - expect(wrapper.find(Alert)).toHaveLength(1); - }); - }); -}); diff --git a/src/scenes/Office/Ppm/WeightsPanel.jsx b/src/scenes/Office/Ppm/WeightsPanel.jsx deleted file mode 100644 index 0ad5cc8f78d..00000000000 --- a/src/scenes/Office/Ppm/WeightsPanel.jsx +++ /dev/null @@ -1,65 +0,0 @@ -import React from 'react'; -import { connect } from 'react-redux'; - -import { - selectAllDocumentsForMove, - findOKedVehicleWeightTickets, - findOKedProgearWeightTickets, - findPendingWeightTickets, -} from 'shared/Entities/modules/moveDocuments'; -import Alert from 'shared/Alert'; -import { PanelField, editablePanelify } from 'shared/EditablePanel'; -import { formatWeight } from 'utils/formatters'; - -function sumWeights(moveDocs) { - return moveDocs.reduce(function (sum, { empty_weight, full_weight }) { - // empty_weight and full_weight can be blank - empty_weight = empty_weight || 0; - full_weight = full_weight || 0; - - // Minimize the damage from having an empty_weight that is larger than the full_weight. - if (empty_weight > full_weight) { - return 0; - } - - return sum + full_weight - empty_weight; - }, 0); -} - -const WeightDisplay = ({ - hasPendingWeightTickets, - ppmPaymentRequestedFlag, - vehicleWeightTicketWeight, - progearWeightTicketWeight, -}) => { - return ( - <> - {ppmPaymentRequestedFlag && hasPendingWeightTickets && ( -
- There are more weight tickets awaiting review. -
- )} -
- -
-
- -
- - ); -}; - -const WeightPanel = editablePanelify(WeightDisplay, null, false); - -function mapStateToProps(state, ownProps) { - const moveDocs = selectAllDocumentsForMove(state, ownProps.moveId); - - return { - ppmPaymentRequestedFlag: true, - vehicleWeightTicketWeight: sumWeights(findOKedVehicleWeightTickets(moveDocs)), - progearWeightTicketWeight: sumWeights(findOKedProgearWeightTickets(moveDocs)), - hasPendingWeightTickets: findPendingWeightTickets(moveDocs).length > 0, - }; -} - -export default connect(mapStateToProps)(WeightPanel); diff --git a/src/scenes/Office/Ppm/api.js b/src/scenes/Office/Ppm/api.js deleted file mode 100644 index d8eddfbb9f5..00000000000 --- a/src/scenes/Office/Ppm/api.js +++ /dev/null @@ -1,15 +0,0 @@ -import { getClient, checkResponse } from 'shared/Swagger/api'; -import { formatDateForSwagger } from 'shared/dates'; - -export async function GetPpmIncentive(moveDate, originZip, originDutyLocationZip, ordersID, weight) { - const client = await getClient(); - const response = await client.apis.ppm.showPPMIncentive({ - original_move_date: formatDateForSwagger(moveDate), - origin_zip: originZip, - origin_duty_location_zip: originDutyLocationZip, - orders_id: ordersID, - weight, - }); - checkResponse(response, 'failed to update ppm due to server error'); - return response.body; -} diff --git a/src/scenes/Office/Ppm/ducks.js b/src/scenes/Office/Ppm/ducks.js deleted file mode 100644 index f12ee8fa2a9..00000000000 --- a/src/scenes/Office/Ppm/ducks.js +++ /dev/null @@ -1,74 +0,0 @@ -import { get, filter } from 'lodash'; -import reduceReducers from 'reduce-reducers'; - -import { GetPpmIncentive } from './api.js'; - -import * as ReduxHelpers from 'shared/ReduxHelpers'; -import { MOVE_DOC_STATUS } from 'shared/constants'; - -const GET_PPM_INCENTIVE = 'GET_PPM_INCENTIVE'; -const GET_PPM_EXPENSE_SUMMARY = 'GET_PPM_EXPENSE_SUMMARY'; -const CLEAR_PPM_INCENTIVE = 'CLEAR_PPM_INCENTIVE'; -export const getIncentiveActionType = ReduxHelpers.generateAsyncActionTypes(GET_PPM_INCENTIVE); -export const getPpmIncentive = ReduxHelpers.generateAsyncActionCreator(GET_PPM_INCENTIVE, GetPpmIncentive); - -const summaryReducer = ReduxHelpers.generateAsyncReducer(GET_PPM_EXPENSE_SUMMARY, (v) => { - return { - summary: { ...v }, - }; -}); - -export const clearPpmIncentive = () => ({ type: CLEAR_PPM_INCENTIVE }); - -export const getTabularExpenses = (expenseData, movingExpenseSchema) => { - if (!expenseData || !movingExpenseSchema) return []; - const expenses = movingExpenseSchema.enum.map((type) => { - const item = expenseData.categories.find((item) => item.category === type); - if (!item) - return { - type: get(movingExpenseSchema['x-display-value'], type), - GTCC: null, - other: null, - total: null, - }; - return { - type: get(movingExpenseSchema['x-display-value'], type), - GTCC: get(item, 'payment_methods.GTCC', null), - other: get(item, 'payment_methods.OTHER', null), - total: item.total, - }; - }); - expenses.push({ - type: 'Total', - GTCC: get(expenseData, 'grand_total.payment_method_totals.GTCC'), - other: get(expenseData, 'grand_total.payment_method_totals.OTHER'), - total: get(expenseData, 'grand_total.total'), - }); - return expenses; -}; - -export const getDocsByStatusAndType = (documents, statusToExclude, typeToExclude) => { - return filter(documents, (expense) => { - if (!statusToExclude) { - return expense.move_document_type !== typeToExclude; - } - if (!typeToExclude) { - return ![MOVE_DOC_STATUS.EXCLUDE, statusToExclude].includes(expense.status); - } - - return ( - ![MOVE_DOC_STATUS.EXCLUDE, statusToExclude].includes(expense.status) && - expense.move_document_type !== typeToExclude - ); - }); -}; - -function clearReducer(state, action) { - if (action.type === CLEAR_PPM_INCENTIVE) return { ...state, calculation: null }; - return state; -} -const incentiveReducer = ReduxHelpers.generateAsyncReducer(GET_PPM_INCENTIVE, (v) => ({ - calculation: { ...v }, -})); - -export default reduceReducers(clearReducer, incentiveReducer, summaryReducer); diff --git a/src/scenes/Office/Ppm/ducks.test.js b/src/scenes/Office/Ppm/ducks.test.js deleted file mode 100644 index d0d47bf9d9d..00000000000 --- a/src/scenes/Office/Ppm/ducks.test.js +++ /dev/null @@ -1,266 +0,0 @@ -import reducer, { getDocsByStatusAndType, getIncentiveActionType, getTabularExpenses } from './ducks'; - -describe('office ppm reducer', () => { - describe('GET_PPM_INCENTIVE', () => { - it('handles SUCCESS', () => { - const newState = reducer(null, { - type: getIncentiveActionType.success, - payload: { gcc: 123400, incentive_percentage: 12400 }, - }); - - expect(newState).toEqual({ - isLoading: false, - hasErrored: false, - hasSucceeded: true, - calculation: { gcc: 123400, incentive_percentage: 12400 }, - }); - }); - it('handles START', () => { - const newState = reducer(null, { - type: getIncentiveActionType.start, - }); - expect(newState).toEqual({ - isLoading: true, - hasErrored: false, - hasSucceeded: false, - }); - }); - it('handles FAILURE', () => { - const newState = reducer(null, { - type: getIncentiveActionType.failure, - }); - expect(newState).toEqual({ - isLoading: false, - hasErrored: true, - hasSucceeded: false, - }); - }); - }); - describe('CLEAR_PPM_INCENTIVE', () => { - it('handles SUCCESS', () => { - const newState = reducer(null, { - type: 'CLEAR_PPM_INCENTIVE', - }); - - expect(newState).toEqual({ - calculation: null, - }); - }); - }); -}); -describe('getTabularExpenses', () => { - const schema = { - type: 'string', - title: 'Moving Expense Type', - enum: [ - 'CONTRACTED_EXPENSE', - 'RENTAL_EQUIPMENT', - 'PACKING_MATERIALS', - 'WEIGHING_FEE', - 'GAS', - 'TOLLS', - 'OIL', - 'OTHER', - ], - 'x-display-value': { - CONTRACTED_EXPENSE: 'Contracted Expense', - RENTAL_EQUIPMENT: 'Rental Equipment', - PACKING_MATERIALS: 'Packing Materials', - WEIGHING_FEE: 'Weighing Fees', - GAS: 'Gas', - TOLLS: 'Tolls', - OIL: 'Oil', - OTHER: 'Other', - }, - }; - describe('when there is no expense data', () => { - it('return and empty array', () => { - expect(getTabularExpenses(null, null)).toEqual([]); - }); - }); - describe('when there are a few categories', () => { - const expenseData = { - categories: [ - { - category: 'CONTRACTED_EXPENSE', - payment_methods: { - GTCC: 600, - }, - total: 600, - }, - { - category: 'RENTAL_EQUIPMENT', - payment_methods: { - OTHER: 500, - }, - total: 500, - }, - { - category: 'TOLLS', - payment_methods: { - OTHER: 500, - }, - total: 500, - }, - ], - grand_total: { - payment_method_totals: { - GTCC: 600, - OTHER: 1000, - }, - total: 1600, - }, - }; - const result = getTabularExpenses(expenseData, schema); - it('should fill in all categories', () => { - expect(result.map((r) => r.type)).toEqual([ - 'Contracted Expense', - 'Rental Equipment', - 'Packing Materials', - 'Weighing Fees', - 'Gas', - 'Tolls', - 'Oil', - 'Other', - 'Total', - ]); - }); - it('should extract GTCC', () => { - expect(result[0].GTCC).toEqual(600); - }); - it('should extract other', () => { - expect(result[1].other).toEqual(500); - }); - - it('should include total as last object in array', () => { - expect(result[result.length - 1]).toEqual({ - GTCC: 600, - other: 1000, - total: 1600, - type: 'Total', - }); - }); - - it('should reshape by category', () => { - expect(result).toEqual([ - { GTCC: 600, other: null, total: 600, type: 'Contracted Expense' }, - { - GTCC: null, - other: 500, - total: 500, - type: 'Rental Equipment', - }, - { - GTCC: null, - other: null, - total: null, - type: 'Packing Materials', - }, - { - GTCC: null, - other: null, - total: null, - type: 'Weighing Fees', - }, - { GTCC: null, other: null, total: null, type: 'Gas' }, - { GTCC: null, other: 500, total: 500, type: 'Tolls' }, - { GTCC: null, other: null, total: null, type: 'Oil' }, - { GTCC: null, other: null, total: null, type: 'Other' }, - { GTCC: 600, other: 1000, total: 1600, type: 'Total' }, - ]); - }); - }); - describe('getDocsByStatusAndType', () => { - it('should filter documents by status and type to exclude', () => { - const documents = [ - { - move_document_type: 'EXPENSE', - status: 'AWAITING_REVIEW', - }, - { - move_document_type: 'STORAGE', - status: 'HAS_ISSUE', - }, - { - move_document_type: 'EXPENSE', - status: 'OK', - }, - { - move_document_type: 'STORAGE', - status: 'OK', - }, - ]; - const filteredDocs = getDocsByStatusAndType(documents, 'OK', 'STORAGE'); - expect(filteredDocs).toEqual([ - { - move_document_type: 'EXPENSE', - status: 'AWAITING_REVIEW', - }, - ]); - }); - - it('should filter documents by status to exclude when a type is missing', () => { - const documents = [ - { - move_document_type: 'EXPENSE', - status: 'AWAITING_REVIEW', - }, - { - move_document_type: 'STORAGE', - status: 'HAS_ISSUE', - }, - { - move_document_type: 'EXPENSE', - status: 'OK', - }, - { - move_document_type: 'STORAGE', - status: 'OK', - }, - ]; - const filteredDocs = getDocsByStatusAndType(documents, 'OK'); - expect(filteredDocs).toEqual([ - { - move_document_type: 'EXPENSE', - status: 'AWAITING_REVIEW', - }, - { - move_document_type: 'STORAGE', - status: 'HAS_ISSUE', - }, - ]); - }); - - it('should filter documents by type to exclude when a status is missing', () => { - const documents = [ - { - move_document_type: 'EXPENSE', - status: 'AWAITING_REVIEW', - }, - { - move_document_type: 'STORAGE', - status: 'HAS_ISSUE', - }, - { - move_document_type: 'EXPENSE', - status: 'OK', - }, - { - move_document_type: 'STORAGE', - status: 'OK', - }, - ]; - const filteredDocs = getDocsByStatusAndType(documents, null, 'STORAGE'); - expect(filteredDocs).toEqual([ - { - move_document_type: 'EXPENSE', - status: 'AWAITING_REVIEW', - }, - { - move_document_type: 'EXPENSE', - status: 'OK', - }, - ]); - }); - }); -}); diff --git a/src/scenes/PpmLanding/MoveSummary/ApprovedMoveSummary.jsx b/src/scenes/PpmLanding/MoveSummary/ApprovedMoveSummary.jsx deleted file mode 100644 index a4177dd5ba1..00000000000 --- a/src/scenes/PpmLanding/MoveSummary/ApprovedMoveSummary.jsx +++ /dev/null @@ -1,100 +0,0 @@ -import React from 'react'; -import { connect } from 'react-redux'; -import { Link } from 'react-router-dom'; - -import ppmCar from 'scenes/PpmLanding/images/ppm-car.svg'; -import PPMStatusTimeline from 'scenes/PpmLanding/PPMStatusTimeline'; -import PpmMoveDetails from 'scenes/PpmLanding/MoveSummary/PpmMoveDetails'; -import { selectPPMCloseoutDocumentsForMove } from 'shared/Entities/modules/movingExpenseDocuments'; - -const ApprovedMoveSummary = ({ ppm, move, weightTicketSets, isMissingWeightTicketDocuments, netWeight }) => { - const paymentRequested = ppm.status === 'PAYMENT_REQUESTED'; - const ppmPaymentRequestIntroRoute = `moves/${move.id}/ppm-payment-request-intro`; - const ppmPaymentRequestReviewRoute = `moves/${move.id}/ppm-payment-review`; - return ( -
-
-
- ppm-car - Handle your own move (PPM) -
- -
- -
-
- {paymentRequested ? ( - isMissingWeightTicketDocuments ? ( -
-
Next step: Contact the PPPO office
-
- You will need to go into the PPPO office in order to take care of your missing weight ticket. -
- - Edit Payment Request - -
- ) : ( -
-
What's next?
-
- We'll email you a link so you can see and download your final payment paperwork. -
-
- We've also sent your paperwork to Finance. They'll review it, determine a final amount, then send - your payment. -
- - Edit Payment Request - -
- ) - ) : ( -
- {weightTicketSets.length ? ( - <> -
Next Step: Finish requesting payment
-
Continue uploading your weight tickets and expense to get paid after your move is done.
- - Continue Requesting Payment - - - ) : ( - <> -
Next Step: Request payment
-
- Request a PPM payment, a storage payment, or an advance against your PPM payment before your - move is done. -
- - Request Payment - - - )} -
- )} -
-
- -
-
-
-
-
-
- ); -}; - -const mapStateToProps = (state, { move }) => ({ - weightTicketSets: selectPPMCloseoutDocumentsForMove(state, move.id, ['WEIGHT_TICKET_SET']), -}); - -export default connect(mapStateToProps)(ApprovedMoveSummary); diff --git a/src/scenes/PpmLanding/MoveSummary/CanceledMoveSummary.jsx b/src/scenes/PpmLanding/MoveSummary/CanceledMoveSummary.jsx deleted file mode 100644 index 5854cb108ee..00000000000 --- a/src/scenes/PpmLanding/MoveSummary/CanceledMoveSummary.jsx +++ /dev/null @@ -1,45 +0,0 @@ -import React from 'react'; -import { get } from 'lodash'; - -import truck from 'shared/icon/truck-gray.svg'; - -const CanceledMoveSummary = (props) => { - const { profile, reviewProfile } = props; - const currentLocation = get(profile, 'current_location'); - const officePhone = get(currentLocation, 'transportation_office.phone_lines.0'); - return ( -
-

New move

-
-
-
-
- ppm-car - Start here -
- -
-
-
-
-
- Make sure you have a copy of your move orders before you get started. Questions or need to help? - Contact your local Transportation Office (PPPO) at {get(currentLocation, 'name')} - {officePhone ? ` at ${officePhone}` : ''}. -
-
-
-
-
- -
-
-
-
-
- ); -}; - -export default CanceledMoveSummary; diff --git a/src/scenes/PpmLanding/MoveSummary/DraftMoveSummary.jsx b/src/scenes/PpmLanding/MoveSummary/DraftMoveSummary.jsx deleted file mode 100644 index 7fabbf21374..00000000000 --- a/src/scenes/PpmLanding/MoveSummary/DraftMoveSummary.jsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; - -import { ProfileStatusTimeline } from 'scenes/PpmLanding/StatusTimeline'; -import truck from 'shared/icon/truck-gray.svg'; - -const DraftMoveSummary = (props) => { - const { profile, resumeMove } = props; - return ( -
-
-
- ppm-car - Move to be scheduled -
- -
-
- -
-
-
-
Next Step: Finish setting up your move
-
- Questions or need help? Contact your local Transportation Office (PPPO) at{' '} - {profile.current_location.name}. -
-
-
-
-
-
Details
-
No details
-
-
-
Documents
-
No documents
-
-
-
-
- -
-
-
-
-
- ); -}; - -export default DraftMoveSummary; diff --git a/src/scenes/PpmLanding/MoveSummary/FindWeightScales.jsx b/src/scenes/PpmLanding/MoveSummary/FindWeightScales.jsx deleted file mode 100644 index 2818f5c26ab..00000000000 --- a/src/scenes/PpmLanding/MoveSummary/FindWeightScales.jsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; - -const FindWeightScales = () => ( - - - Find Certified Weight Scales - - -); - -export default FindWeightScales; diff --git a/src/scenes/PpmLanding/MoveSummary/PaymentRequestedSummary.jsx b/src/scenes/PpmLanding/MoveSummary/PaymentRequestedSummary.jsx deleted file mode 100644 index 45e042faa26..00000000000 --- a/src/scenes/PpmLanding/MoveSummary/PaymentRequestedSummary.jsx +++ /dev/null @@ -1,63 +0,0 @@ -import React from 'react'; -import moment from 'moment'; - -import { ppmInfoPacket } from 'shared/constants'; -import ppmCar from 'scenes/PpmLanding/images/ppm-car.svg'; -import PPMStatusTimeline from 'scenes/PpmLanding/PPMStatusTimeline'; -import FindWeightScales from 'scenes/PpmLanding/MoveSummary/FindWeightScales'; -import PpmMoveDetails from 'scenes/PpmLanding/MoveSummary/SubmittedPpmMoveDetails'; - -const PaymentRequestedSummary = (props) => { - const { ppm } = props; - const moveInProgress = moment(ppm.original_move_date, 'YYYY-MM-DD').isSameOrBefore(); - return ( -
-
-
- ppm-car - Handle your own move (PPM) -
- -
- -
-
- {!moveInProgress && ( -
-
Next Step: Get ready to move
-
- Remember to save your weight tickets and expense receipts. For more information, read the PPM info - packet. -
- - - -
- )} -
-
Your payment is in review
-
You will receive a notification from your destination PPPO office when it has been reviewed.
-
-
-
- -
-
Documents
- -
-
-
-
- -
-
-
-
- ); -}; - -export default PaymentRequestedSummary; diff --git a/src/scenes/PpmLanding/MoveSummary/PpmMoveDetails.jsx b/src/scenes/PpmLanding/MoveSummary/PpmMoveDetails.jsx deleted file mode 100644 index 17517ce048b..00000000000 --- a/src/scenes/PpmLanding/MoveSummary/PpmMoveDetails.jsx +++ /dev/null @@ -1,85 +0,0 @@ -import React from 'react'; -import { connect } from 'react-redux'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; - -import styles from './PpmMoveDetails.module.scss'; - -import { formatCentsRange, formatCents } from 'utils/formatters'; -import { getIncentiveRange } from 'utils/incentives'; -import { selectPPMEstimateRange, selectReimbursementById } from 'store/entities/selectors'; -import { selectPPMCloseoutDocumentsForMove } from 'shared/Entities/modules/movingExpenseDocuments'; -import ToolTip from 'shared/ToolTip/ToolTip'; - -const PpmMoveDetails = ({ advance, ppm, isMissingWeightTicketDocuments, estimateRange, netWeight }) => { - const privateStorageString = ppm.estimated_storage_reimbursement - ? `(up to ${ppm.estimated_storage_reimbursement})` - : ''; - const advanceString = - ppm.has_requested_advance && advance && advance.requested_amount - ? `Advance Requested: $${formatCents(advance.requested_amount)}` - : ''; - const hasSitString = `Temp. Storage: ${ppm.days_in_storage} days ${privateStorageString}`; - const estimatedIncentiveRange = getIncentiveRange(ppm, estimateRange); - const actualIncentiveRange = formatCentsRange(estimateRange?.range_min, estimateRange?.range_max); - - const hasRangeReady = ppm.incentive_estimate_min || estimatedIncentiveRange; - - const incentiveNotReady = () => { - return ( - <> - Not ready yet{' '} - - - ); - }; - - return ( -
-
Estimated
-
Weight: {ppm.weight_estimate} lbs
- {hasRangeReady && isMissingWeightTicketDocuments ? ( - <> -
- Unknown - -
-
- Estimated payment will be given after resolving missing weight tickets. -
- - ) : ( - <> -
-
Payment: {hasRangeReady ? estimatedIncentiveRange : incentiveNotReady()}
-
- {ppm.status === 'PAYMENT_REQUESTED' && ( -
-
Submitted
-
Weight: {netWeight} lbs
-
Payment request: {hasRangeReady ? actualIncentiveRange : incentiveNotReady()}
-
- )} -
- Actual payment may vary, subject to Finance review. -
- - )} - {ppm.has_sit &&
{hasSitString}
} - {ppm.has_requested_advance &&
{advanceString}
} -
- ); -}; - -const mapStateToProps = (state, ownProps) => { - const advance = selectReimbursementById(state, ownProps.ppm.advance) || {}; - const isMissingWeightTicketDocuments = selectPPMCloseoutDocumentsForMove(state, ownProps.ppm.move_id, [ - 'WEIGHT_TICKET_SET', - ]).some((doc) => doc.empty_weight_ticket_missing || doc.full_weight_ticket_missing); - return { - advance, - isMissingWeightTicketDocuments, - estimateRange: selectPPMEstimateRange(state), - }; -}; - -export default connect(mapStateToProps)(PpmMoveDetails); diff --git a/src/scenes/PpmLanding/MoveSummary/PpmMoveDetails.module.scss b/src/scenes/PpmLanding/MoveSummary/PpmMoveDetails.module.scss deleted file mode 100644 index d5d91945645..00000000000 --- a/src/scenes/PpmLanding/MoveSummary/PpmMoveDetails.module.scss +++ /dev/null @@ -1,13 +0,0 @@ -@import 'shared/styles/themes.scss'; - -.subText{ - fontSize: '0.90em'; - color: $subtext; -} -.payment-details{ - margin-top: 1em; -} - -.detail-title{ - font-weight: bold; -} \ No newline at end of file diff --git a/src/scenes/PpmLanding/MoveSummary/SubmittedPpmMoveDetails.jsx b/src/scenes/PpmLanding/MoveSummary/SubmittedPpmMoveDetails.jsx deleted file mode 100644 index f8ba6a5fe5c..00000000000 --- a/src/scenes/PpmLanding/MoveSummary/SubmittedPpmMoveDetails.jsx +++ /dev/null @@ -1,63 +0,0 @@ -import React from 'react'; -import { connect } from 'react-redux'; - -import styles from './PpmMoveDetails.module.scss'; - -import { formatCents } from 'utils/formatters'; -import { getIncentiveRange } from 'utils/incentives'; -import { selectPPMCloseoutDocumentsForMove } from 'shared/Entities/modules/movingExpenseDocuments'; -import { selectCurrentPPM, selectPPMEstimateRange, selectReimbursementById } from 'store/entities/selectors'; -import { selectPPMEstimateError } from 'store/onboarding/selectors'; -import ToolTip from 'shared/ToolTip/ToolTip'; - -const SubmittedPpmMoveDetails = (props) => { - const { advance, currentPPM, hasEstimateError, estimateRange } = props; - const privateStorageString = currentPPM?.estimated_storage_reimbursement - ? `(up to ${currentPPM.estimated_storage_reimbursement})` - : ''; - const advanceString = currentPPM?.has_requested_advance - ? `Advance Requested: $${formatCents(advance.requested_amount)}` - : ''; - const hasSitString = `Temp. Storage: ${currentPPM?.days_in_storage} days ${privateStorageString}`; - const incentiveRange = getIncentiveRange(currentPPM, estimateRange); - - const weightEstimate = currentPPM?.weight_estimate; - return ( -
-
Estimated
-
Weight: {weightEstimate} lbs
-
- Payment:{' '} - {!incentiveRange || hasEstimateError ? ( - <> - Not ready yet{' '} - - - ) : ( - incentiveRange - )} -
- {currentPPM?.has_sit &&
{hasSitString}
} - {currentPPM?.has_requested_advance &&
{advanceString}
} -
- ); -}; - -const mapStateToProps = (state) => { - const currentPPM = selectCurrentPPM(state) || {}; - const advance = selectReimbursementById(state, currentPPM?.advance) || {}; - const isMissingWeightTicketDocuments = selectPPMCloseoutDocumentsForMove(state, currentPPM?.move_id, [ - 'WEIGHT_TICKET_SET', - ]).some((doc) => doc.empty_weight_ticket_missing || doc.full_weight_ticket_missing); - - const props = { - currentPPM, - advance, - isMissingWeightTicketDocuments, - estimateRange: selectPPMEstimateRange(state) || {}, - hasEstimateError: selectPPMEstimateError(state), - }; - return props; -}; - -export default connect(mapStateToProps)(SubmittedPpmMoveDetails); diff --git a/src/scenes/PpmLanding/MoveSummary/SubmittedPpmMoveSummary.jsx b/src/scenes/PpmLanding/MoveSummary/SubmittedPpmMoveSummary.jsx deleted file mode 100644 index e51c50e406d..00000000000 --- a/src/scenes/PpmLanding/MoveSummary/SubmittedPpmMoveSummary.jsx +++ /dev/null @@ -1,66 +0,0 @@ -import React from 'react'; - -import { ppmInfoPacket } from 'shared/constants'; -import ppmCar from 'scenes/PpmLanding/images/ppm-car.svg'; -import PPMStatusTimeline from 'scenes/PpmLanding/PPMStatusTimeline'; -import FindWeightScales from 'scenes/PpmLanding/MoveSummary/FindWeightScales'; -import PpmMoveDetails from 'scenes/PpmLanding/MoveSummary/SubmittedPpmMoveDetails'; - -const SubmittedPpmMoveSummary = (props) => { - const { ppm, hasEstimateError } = props; - return ( -
-
- ppm-car - Handle your own move (PPM) -
-
- -
-
-
-
Next Step: Wait for approval & get ready
-
- You'll be notified when your move is approved (up to 5 days). To get ready to move: - -
-
-
-
- -
-
Documents
- -
-
-
- - Read PPM Info Sheet - -
- -
-
-
- ); -}; - -export default SubmittedPpmMoveSummary; diff --git a/src/scenes/PpmLanding/PPMStatusTimeline.js b/src/scenes/PpmLanding/PPMStatusTimeline.js deleted file mode 100644 index 16d40ddc67b..00000000000 --- a/src/scenes/PpmLanding/PPMStatusTimeline.js +++ /dev/null @@ -1,146 +0,0 @@ -import React from 'react'; -import { get, includes } from 'lodash'; -import moment from 'moment'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; - -import { StatusTimeline } from './StatusTimeline'; - -import { - getSignedCertification, - selectPaymentRequestCertificationForMove, -} from 'shared/Entities/modules/signed_certifications'; -import { selectCurrentMove } from 'store/entities/selectors'; -import { milmoveLogger } from 'utils/milmoveLog'; - -const PpmStatuses = { - Submitted: 'SUBMITTED', - Approved: 'APPROVED', - PaymentRequested: 'PAYMENT_REQUESTED', - Completed: 'COMPLETED', -}; - -const PpmStatusTimelineCodes = { - Submitted: 'SUBMITTED', - PpmApproved: 'PPM_APPROVED', - InProgress: 'IN_PROGRESS', - PaymentRequested: 'PAYMENT_REQUESTED', - PaymentReviewed: 'PAYMENT_REVIEWED', -}; - -export class PPMStatusTimeline extends React.Component { - componentDidMount() { - const { moveId } = this.props; - this.props.getSignedCertification(moveId); - } - - static determineActualMoveDate(ppm) { - const approveDate = get(ppm, 'approve_date'); - const originalMoveDate = get(ppm, 'original_move_date'); - const actualMoveDate = get(ppm, 'actual_move_date'); - // if there's no approve date, then the PPM hasn't been approved yet - // and the in progress date should not be shown - if (!approveDate) { - return undefined; - } - // if there's an actual move date that is known and passed, show it - // else show original move date if it has passed - if (actualMoveDate && moment(actualMoveDate, 'YYYY-MM-DD').isSameOrBefore()) { - return actualMoveDate; - } - if (moment(originalMoveDate, 'YYYY-MM-DD').isSameOrBefore()) { - return originalMoveDate; - } - return undefined; - } - - isCompleted(statusCode) { - const { ppm } = this.props; - const moveIsApproved = includes( - [PpmStatuses.Approved, PpmStatuses.PaymentRequested, PpmStatuses.Completed], - ppm.status, - ); - const moveInProgress = moment(ppm.original_move_date, 'YYYY-MM-DD').isSameOrBefore(); - const moveIsComplete = includes([PpmStatuses.PaymentRequested, PpmStatuses.Completed], ppm.status); - - switch (statusCode) { - case PpmStatusTimelineCodes.Submitted: - return true; - case PpmStatusTimelineCodes.Approved: - return moveIsApproved; - case PpmStatusTimelineCodes.InProgress: - return (moveInProgress && ppm.status === PpmStatuses.Approved) || moveIsComplete; - case PpmStatusTimelineCodes.PaymentRequested: - return moveIsComplete; - case PpmStatusTimelineCodes.PaymentReviewed: - return ppm.status === PpmStatuses.Completed; - default: - milmoveLogger.warn(`Unknown status: ${statusCode}`); - } - return undefined; - } - - getStatuses() { - const { ppm, signedCertification } = this.props; - const actualMoveDate = PPMStatusTimeline.determineActualMoveDate(ppm); - const approveDate = get(ppm, 'approve_date'); - const submitDate = get(ppm, 'submit_date'); - const paymentRequestedDate = signedCertification && signedCertification.date ? signedCertification.date : null; - return [ - { - name: 'Submitted', - code: PpmStatusTimelineCodes.Submitted, - dates: [submitDate], - completed: this.isCompleted(PpmStatusTimelineCodes.Submitted), - }, - { - name: 'Approved', - code: PpmStatusTimelineCodes.PpmApproved, - dates: [approveDate], - completed: this.isCompleted(PpmStatusTimelineCodes.Approved), - }, - { - name: 'In progress', - code: PpmStatusTimelineCodes.InProgress, - dates: [actualMoveDate], - completed: this.isCompleted(PpmStatusTimelineCodes.InProgress), - }, - { - name: 'Payment requested', - code: PpmStatusTimelineCodes.PaymentRequested, - dates: [paymentRequestedDate], - completed: this.isCompleted(PpmStatusTimelineCodes.PaymentRequested), - }, - { - name: 'Payment reviewed', - code: PpmStatusTimelineCodes.PaymentReviewed, - completed: this.isCompleted(PpmStatusTimelineCodes.PaymentReviewed), - }, - ]; - } - - render() { - const statuses = this.getStatuses(); - return ; - } -} - -PPMStatusTimeline.propTypes = { - ppm: PropTypes.object.isRequired, -}; - -function mapStateToProps(state) { - const move = selectCurrentMove(state); - const moveId = move?.id; - - return { - signedCertification: selectPaymentRequestCertificationForMove(state, moveId), - moveId, - }; -} - -const mapDispatchToProps = { - getSignedCertification, -}; - -export default connect(mapStateToProps, mapDispatchToProps)(PPMStatusTimeline); diff --git a/src/scenes/PpmLanding/PpmAlert.jsx b/src/scenes/PpmLanding/PpmAlert.jsx deleted file mode 100644 index b3076b4282b..00000000000 --- a/src/scenes/PpmLanding/PpmAlert.jsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react'; - -import Alert from 'shared/Alert'; -import { ppmInfoPacket } from 'shared/constants'; - -const PpmAlert = (props) => { - return ( - - Next, wait for approval. Once approved: -
-
    -
  • - Get certified weight tickets, both empty & full -
  • -
  • - Save expense receipts, including for storage -
  • -
  • - Read the{' '} - - - PPM info sheet - - {' '} - for more info -
  • -
-
- ); -}; - -export default PpmAlert; diff --git a/src/scenes/PpmLanding/PpmSummary.jsx b/src/scenes/PpmLanding/PpmSummary.jsx deleted file mode 100644 index bdde20e464b..00000000000 --- a/src/scenes/PpmLanding/PpmSummary.jsx +++ /dev/null @@ -1,196 +0,0 @@ -import React from 'react'; -import { connect } from 'react-redux'; -import { get, includes, isEmpty } from 'lodash'; -import classnames from 'classnames'; - -import Alert from 'shared/Alert'; -import TransportationOfficeContactInfo from 'shared/TransportationOffices/TransportationOfficeContactInfo'; -import { selectPPMCloseoutDocumentsForMove } from 'shared/Entities/modules/movingExpenseDocuments'; -import { getMoveDocumentsForMove } from 'shared/Entities/modules/moveDocuments'; -import { calcNetWeight } from 'scenes/Moves/Ppm/utility'; -import ApprovedMoveSummary from 'scenes/PpmLanding/MoveSummary/ApprovedMoveSummary'; -import CanceledMoveSummary from 'scenes/PpmLanding/MoveSummary/CanceledMoveSummary'; -import DraftMoveSummary from 'scenes/PpmLanding/MoveSummary/DraftMoveSummary'; -import PaymentRequestedSummary from 'scenes/PpmLanding/MoveSummary/PaymentRequestedSummary'; -import SubmittedPpmMoveSummary from 'scenes/PpmLanding/MoveSummary/SubmittedPpmMoveSummary'; -import { selectServiceMemberFromLoggedInUser } from 'store/entities/selectors'; -import { calculatePPMEstimate } from 'services/internalApi'; -import { updatePPMEstimate } from 'store/entities/actions'; -import { setPPMEstimateError } from 'store/onboarding/actions'; -import styles from 'scenes/PpmLanding/PpmSummary.module.css'; - -const MoveInfoHeader = (props) => { - const { orders, profile, move, entitlement } = props; - return ( -
-

- {get(orders, 'new_duty_location.name', 'New move')} (from {get(profile, 'current_location.name', '')}) -

- {get(move, 'locator') &&
Move Locator: {get(move, 'locator')}
} - {!isEmpty(entitlement) && ( -
- Weight allowance:{' '} - {entitlement.weight.toLocaleString()} lbs -
- )} -
- ); -}; - -const genPpmSummaryStatusComponents = { - DRAFT: DraftMoveSummary, - SUBMITTED: SubmittedPpmMoveSummary, - APPROVED: ApprovedMoveSummary, - CANCELED: CanceledMoveSummary, - PAYMENT_REQUESTED: PaymentRequestedSummary, -}; - -const getPPMStatus = (moveStatus, ppm) => { - // PPM status determination - const ppmStatus = get(ppm, 'status', 'DRAFT'); - return moveStatus === 'APPROVED' && (ppmStatus === 'SUBMITTED' || ppmStatus === 'DRAFT') ? 'SUBMITTED' : moveStatus; -}; - -export class PpmSummaryComponent extends React.Component { - constructor(props) { - super(props); - - this.state = { - hasEstimateError: false, - netWeight: null, - }; - } - - componentDidMount() { - if (this.props.move.id) { - this.props.getMoveDocumentsForMove(this.props.move.id).then(({ obj: documents }) => { - const weightTicketNetWeight = calcNetWeight(documents); - let netWeight = - weightTicketNetWeight > this.props.entitlement.sum ? this.props.entitlement.sum : weightTicketNetWeight; - - if (netWeight === 0) { - netWeight = this.props.ppm.weight_estimate; - } - if (!netWeight) { - this.setState({ hasEstimateError: true }); - } - if (!isEmpty(this.props.ppm) && netWeight) { - calculatePPMEstimate( - this.props.ppm.original_move_date, - this.props.ppm.pickup_postal_code, - this.props.originDutyLocationZip, - this.props.orders.id, - netWeight, - ) - .then((response) => { - this.props.updatePPMEstimate(response); - this.props.setPPMEstimateError(null); - }) - .catch((err) => { - this.props.setPPMEstimateError(err); - this.setState({ hasEstimateError: true }); - }); - - this.setState({ netWeight }); - } - }); - } - } - - render() { - const { - profile, - move, - orders, - ppm, - editMove, - entitlement, - resumeMove, - reviewProfile, - isMissingWeightTicketDocuments, - } = this.props; - const moveStatus = get(move, 'status', 'DRAFT'); - const ppmStatus = getPPMStatus(moveStatus, ppm); - const PPMComponent = genPpmSummaryStatusComponents[ppmStatus]; - return ( -
- {move.status === 'CANCELED' && ( - - Your move from {get(profile, 'current_location.name')} to {get(orders, 'new_duty_location.name')} with the - move locator ID {get(move, 'locator')} was canceled. - - )} - -
-
- {move.status !== 'CANCELED' && ( -
- -
-
- )} - {isMissingWeightTicketDocuments && ppm.status === 'PAYMENT_REQUESTED' && ( - - You will need to contact your local PPPO office to resolve your missing weight ticket. - - )} -
-
-
-
- -
- -
-
- -
-
-

Contacts

- -
-
-
-
- ); - } -} - -function mapStateToProps(state, ownProps) { - const serviceMember = selectServiceMemberFromLoggedInUser(state); - const isMissingWeightTicketDocuments = selectPPMCloseoutDocumentsForMove(state, ownProps.move.id, [ - 'WEIGHT_TICKET_SET', - ]).some((doc) => doc.empty_weight_ticket_missing || doc.full_weight_ticket_missing); - - return { - isMissingWeightTicketDocuments, - originDutyLocationZip: serviceMember?.current_location?.address?.postalCode, - }; -} - -const mapDispatchToProps = { - getMoveDocumentsForMove, - updatePPMEstimate, - setPPMEstimateError, -}; - -export const PpmSummary = connect(mapStateToProps, mapDispatchToProps)(PpmSummaryComponent); diff --git a/src/scenes/PpmLanding/PpmSummary.module.css b/src/scenes/PpmLanding/PpmSummary.module.css deleted file mode 100644 index 07c1cdaf262..00000000000 --- a/src/scenes/PpmLanding/PpmSummary.module.css +++ /dev/null @@ -1,142 +0,0 @@ -h2 { - margin-bottom: 0; -} - -.usa-alert li { - margin-bottom: 0; -} - -.usa-alert ul { - padding-left: 3.5rem; -} - -.shipment_box { - border: 3px solid #f1f1f1; - overflow: hidden; - padding-bottom: 4rem; - position: relative; -} - -img.move_sm { - height: 1em; - display: inline; - margin-bottom: 0; - margin-right: 0.3em; - padding: 0rem 1.2rem 0rem 0rem; -} - -img.status_icon { - margin: 1.2em 1rem 1.5rem 1rem; -} - -.shipment_type { - background-color: #f1f1f1; - font-size: 1.1em; - font-weight: bold; - padding: 0.7em; -} - -.shipment_box_contents { - padding: 0.5em; -} - -.step { - margin-bottom: 1em; -} - -.next-step { - width: 102%; -} - -.next-step li { - margin-bottom: 0; -} - -.step-contents { - overflow: hidden; - margin: 1em 1rem 1rem 1rem; -} - -.step-links { - margin: 1em 1rem 1rem 1rem; -} - -.details-links a { - display: block; -} - -.titled_block { - margin-top: 0.5em; -} - -a { - text-decoration: none; -} - -.contact_block { - margin-top: 2em; -} - -.contact_block .title { - font-size: 2.2rem; -} - -.move-summary button.link { - color: #0071bc; - background: transparent; - padding: 0; - font-weight: 400; -} - -.move-summary button.link:hover { - color: #205493; -} - -.ppm-panel button { - background-color: #0071bc; - color: #ffffff; -} - -.ppm-panel button:hover { - background-color: #205493; - color: #ffffff; -} - -.ppm-panel .shipment_box { - padding-bottom: 0; -} - -.ppm-panel .shipment_type { - background-color: #e1f2f8; -} - -.ppm-panel .shipment_box_contents { - background-color: #f5fbfd; -} - -.st-wrapper { - width: 100%; -} - -button.usa-button--secondary { - margin-left: 0; -} - -@media (max-width: 600px) { - .st-wrapper { - width: 100vw; - max-width: 100vw; - min-width: 320px; - margin-left: -1.06667rem; - margin-right: 0; - } - .shipment_box_contents { - padding: 0; - } - .shipment_type { - font-size: 1em; - } - a.usa-button { - margin-left: 16px; - } -} diff --git a/src/scenes/PpmLanding/PpmSummary.test.jsx b/src/scenes/PpmLanding/PpmSummary.test.jsx deleted file mode 100644 index 554d6a4984c..00000000000 --- a/src/scenes/PpmLanding/PpmSummary.test.jsx +++ /dev/null @@ -1,178 +0,0 @@ -import React from 'react'; -import { shallow } from 'enzyme'; -import moment from 'moment'; - -import { PpmSummaryComponent } from './PpmSummary'; - -import CanceledMoveSummary from 'scenes/PpmLanding/MoveSummary/CanceledMoveSummary'; -import DraftMoveSummary from 'scenes/PpmLanding/MoveSummary/DraftMoveSummary'; -import SubmittedPpmMoveSummary from 'scenes/PpmLanding/MoveSummary/SubmittedPpmMoveSummary'; -import { SHIPMENT_OPTIONS } from 'shared/constants'; - -describe('PpmSummaryComponent', () => { - const editMoveFn = jest.fn(); - const resumeMoveFn = jest.fn(); - const entitlementObj = { sum: '10000' }; - const serviceMember = { current_location: { name: 'Ft Carson' } }; - const ordersObj = {}; - const getMoveDocumentsForMove = jest.fn(() => ({ then: () => {} })); - const getShallowRender = (entitlementObj, serviceMember, ordersObj, moveObj, ppmObj, editMoveFn, resumeMoveFn) => { - return shallow( - , - ); - }; - - describe('when a ppm move is in a draft state', () => { - it('renders resume setup content', () => { - const moveObj = { selected_move_type: SHIPMENT_OPTIONS.PPM, status: 'DRAFT' }; - const futureFortNight = moment().add(14, 'day'); - const ppmObj = { - original_move_date: futureFortNight, - weight_estimate: '10000', - estimated_incentive: '$24665.59 - 27261.97', - status: 'CANCELED', - }; - const subComponent = getShallowRender( - entitlementObj, - serviceMember, - ordersObj, - moveObj, - ppmObj, - editMoveFn, - resumeMoveFn, - ); - expect(subComponent.find(DraftMoveSummary).length).toBe(1); - expect(subComponent.find(DraftMoveSummary).dive().find('.step').find('.title').html()).toEqual( - '
Next Step: Finish setting up your move
', - ); - }); - }); - // PPM - describe('when a ppm move is in canceled state', () => { - it('renders cancel content', () => { - const moveObj = { selected_move_type: SHIPMENT_OPTIONS.PPM, status: 'CANCELED' }; - const futureFortNight = moment().add(14, 'day'); - const ppmObj = { - original_move_date: futureFortNight, - weight_estimate: '10000', - estimated_incentive: '$24665.59 - 27261.97', - status: 'CANCELED', - }; - const subComponent = getShallowRender( - entitlementObj, - serviceMember, - ordersObj, - moveObj, - ppmObj, - editMoveFn, - resumeMoveFn, - ); - expect(subComponent.find(CanceledMoveSummary).length).toBe(1); - expect(subComponent.find(CanceledMoveSummary).dive().find('h1').html()).toEqual('

New move

'); - }); - }); - describe('when a move with a ppm is in submitted state', () => { - it('renders submitted content', () => { - const moveObj = { selected_move_type: SHIPMENT_OPTIONS.PPM, status: 'SUBMITTED' }; - const futureFortNight = moment().add(14, 'day'); - const ppmObj = { - original_move_date: futureFortNight, - weight_estimate: '10000', - estimated_incentive: '$24665.59 - 27261.97', - }; - const subComponent = getShallowRender( - entitlementObj, - serviceMember, - ordersObj, - moveObj, - ppmObj, - editMoveFn, - resumeMoveFn, - ).find(SubmittedPpmMoveSummary); - expect(subComponent.find(SubmittedPpmMoveSummary).length).toBe(1); - expect(subComponent.find(SubmittedPpmMoveSummary).dive().find('.step').find('div.title').first().html()).toEqual( - '
Next Step: Wait for approval & get ready
', - ); - }); - }); - - describe('when a move is in approved state but ppm is submitted state', () => { - it('renders submitted rather than approved content', () => { - const moveObj = { selected_move_type: SHIPMENT_OPTIONS.PPM, status: 'APPROVED' }; - const futureFortNight = moment().add(14, 'day'); - const ppmObj = { - original_move_date: futureFortNight, - weight_estimate: '10000', - estimated_incentive: '$24665.59 - 27261.97', - status: 'SUBMITTED', - }; - const subComponent = getShallowRender( - entitlementObj, - serviceMember, - ordersObj, - moveObj, - ppmObj, - editMoveFn, - resumeMoveFn, - ).find(SubmittedPpmMoveSummary); - expect(subComponent.find(SubmittedPpmMoveSummary).length).toBe(1); - expect(subComponent.find(SubmittedPpmMoveSummary).dive().find('.step').find('div.title').first().html()).toEqual( - '
Next Step: Wait for approval & get ready
', - ); - }); - }); - describe('when a move and ppm are in approved state', () => { - it('renders approved content', () => { - const moveObj = { status: 'APPROVED' }; - const futureFortNight = moment().add(14, 'day'); - const ppmObj = { - original_move_date: futureFortNight, - weight_estimate: '10000', - estimated_incentive: '$24665.59 - 27261.97', - status: 'APPROVED', - }; - const component = getShallowRender( - entitlementObj, - serviceMember, - ordersObj, - moveObj, - ppmObj, - editMoveFn, - resumeMoveFn, - ); - const ppmSummary = component.find('Connect(ApprovedMoveSummary)'); - expect(ppmSummary.exists()).toBe(true); - }); - }); - describe('when a move with a ppm is in in progress state', () => { - it('renders in progress content', () => { - const moveObj = { status: 'APPROVED' }; - const pastFortNight = moment().subtract(14, 'day'); - const ppmObj = { - original_move_date: pastFortNight, - weight_estimate: '10000', - estimated_incentive: '$24665.59 - 27261.97', - }; - const component = getShallowRender( - entitlementObj, - serviceMember, - ordersObj, - moveObj, - ppmObj, - editMoveFn, - resumeMoveFn, - ); - const ppmSummary = component.find(SubmittedPpmMoveSummary); - expect(ppmSummary.exists()).toBe(true); - }); - }); -}); diff --git a/src/scenes/PpmLanding/StatusTimeline.jsx b/src/scenes/PpmLanding/StatusTimeline.jsx deleted file mode 100644 index 9feebe22809..00000000000 --- a/src/scenes/PpmLanding/StatusTimeline.jsx +++ /dev/null @@ -1,88 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { PureComponent } from 'react'; -import { filter, findLast } from 'lodash'; - -import { displayDateRange } from 'utils/formatters'; -import './StatusTimeline.scss'; - -function getCurrentStatus(statuses) { - return findLast(statuses, function (status) { - return status.completed; - }); -} - -export class ProfileStatusTimeline extends React.Component { - getStatuses() { - return [ - { name: 'Profile', code: 'PROFILE', completed: true }, - { name: 'Orders', code: 'ORDERS', completed: true }, - { name: 'Move Setup', code: 'MOVE_SETUP', completed: false }, - { name: 'Review', code: 'REVIEW', completed: false }, - ]; - } - - render() { - return ; - } -} - -ProfileStatusTimeline.propTypes = { - profile: PropTypes.object.isRequired, -}; - -export class StatusTimeline extends PureComponent { - createStatusBlock = (status, currentStatus) => { - return ( - { - return date; - })} - completed={status.completed} - current={currentStatus.code === status.code} - /> - ); - }; - - render() { - const currentStatus = getCurrentStatus(this.props.statuses); - const statusBlocks = this.props.statuses.map((status) => this.createStatusBlock(status, currentStatus)); - - return ( -
- {statusBlocks} - {this.props.showEstimated &&
* Estimated
} -
- ); - } -} - -StatusTimeline.propTypes = { - statuses: PropTypes.array.isRequired, -}; - -export const StatusBlock = (props) => { - const classes = ['status_block', props.code.toLowerCase()]; - if (props.completed) classes.push('status_completed'); - if (props.current) classes.push('status_current'); - - return ( -
-
-
{props.name}
- {props.dates && props.dates.length > 0 && ( -
{displayDateRange(props.dates, 'condensed')}
- )} -
- ); -}; - -StatusBlock.propTypes = { - code: PropTypes.string.isRequired, - completed: PropTypes.bool.isRequired, - current: PropTypes.bool.isRequired, - name: PropTypes.string.isRequired, - dates: PropTypes.arrayOf(PropTypes.string), -}; diff --git a/src/scenes/PpmLanding/StatusTimeline.scss b/src/scenes/PpmLanding/StatusTimeline.scss deleted file mode 100644 index e6e645dc40e..00000000000 --- a/src/scenes/PpmLanding/StatusTimeline.scss +++ /dev/null @@ -1,127 +0,0 @@ -@import '../../shared/styles/basics'; -@import '../../shared/styles/mixins'; -@import '../../shared/styles/colors'; - -.status_block.status_current .status_name { - @include u-text('bold'); -} - -.shipment_box_contents .status_timeline { - display: flex; - flex-direction: row; - justify-content: space-between; - margin-top: 2rem; - margin-bottom: 2rem; - text-align: center; - @include u-font-size('body', '3xs'); -} - -.shipment_box_contents .status_block { - flex: 1; - display: flex; - flex-direction: column; - align-items: center; - position: relative; - font-size: 0.9em; -} - -.shipment_box_contents .status_dot { - display: block; - width: 20px; - height: 20px; - background: white; - border: 3px solid $base-lighter; - border-radius: 50%; - -moz-border-radius: 50%; - -webkit-border-radius: 50%; - z-index: 10; -} - -.status_block.packed .status_dates, -.status_block.delivered .status_dates { - font-style: italic; - color: #999; -} -.status_block.packed .status_dates:after, -.status_block.delivered .status_dates:after { - content: ' *'; -} - -.status_block { - color: $base; -} - -.status_block.status_completed, -.status_block.status_current { - color: black; -} - -.status_block.status_completed .status_dot { - background: #102e51; - border: none; - box-shadow: 0px 0px 0px 2px #102e51; -} - -.status_block.status_current .status_dot { - background: #102e51; - border: none; - box-shadow: 0px 0px 0px 4px #0270bc; -} - -.status_block:after { - display: block; - content: ' '; - width: 100%; - height: 3px; - background: #d6d7d9; - position: absolute; - right: 50%; - top: 10px; - z-index: 1; -} - -.status_block:first-child:after { - display: none; -} - -.status_block.status_completed:after, -.status_block.status_current:after { - background: #102e51; -} - -.status_block.status_completed:first-child:after { - display: none; -} - -.shipment_box_contents .status_name { - margin: 1rem 0 0 0; - max-width: 100px; - text-align: center; - line-height: 1.25; -} - -.shipment_box_contents { - padding: 0; -} - -.status_timeline .legend { - font-style: italic; - color: #999; - position: absolute; - bottom: 2rem; - left: 1rem; -} - -.st-wrapper a.usa-button { - margin-left: 16px; - max-width: 90%; -} - -@include at-media(tablet) { - .shipment_box_contents .status_timeline { - @include u-font-size('body', '2xs'); - } - .st-wrapper a.usa-button { - max-width: 100%; - } -} diff --git a/src/scenes/PpmLanding/StatusTimeline.test.jsx b/src/scenes/PpmLanding/StatusTimeline.test.jsx deleted file mode 100644 index 5ad871f012c..00000000000 --- a/src/scenes/PpmLanding/StatusTimeline.test.jsx +++ /dev/null @@ -1,89 +0,0 @@ -import React from 'react'; -import { shallow, mount } from 'enzyme'; - -import { StatusBlock, ProfileStatusTimeline } from './StatusTimeline'; -import { PPMStatusTimeline } from './PPMStatusTimeline'; - -describe('StatusTimeline', () => { - describe('PPMStatusTimeline', () => { - test('renders timeline', () => { - const ppm = {}; - const wrapper = mount(); - - expect(wrapper.find(StatusBlock)).toHaveLength(5); - }); - - test('renders timeline for submitted ppm', () => { - const ppm = { status: 'SUBMITTED' }; - const wrapper = mount(); - - const completed = wrapper.findWhere((b) => b.prop('completed')); - expect(completed).toHaveLength(1); - expect(completed.prop('code')).toEqual('SUBMITTED'); - - const current = wrapper.findWhere((b) => b.prop('current')); - expect(current).toHaveLength(1); - expect(current.prop('code')).toEqual('SUBMITTED'); - }); - - test('renders timeline for an in-progress ppm', () => { - const ppm = { status: 'APPROVED', original_move_date: '2019-03-20' }; - const wrapper = mount(); - - const completed = wrapper.findWhere((b) => b.prop('completed')); - expect(completed).toHaveLength(3); - expect(completed.map((b) => b.prop('code'))).toEqual(['SUBMITTED', 'PPM_APPROVED', 'IN_PROGRESS']); - - const current = wrapper.findWhere((b) => b.prop('current')); - expect(current).toHaveLength(1); - expect(current.prop('code')).toEqual('IN_PROGRESS'); - }); - }); - - describe('ProfileStatusTimeline', () => { - test('renders timeline', () => { - const profile = {}; - const wrapper = mount(); - - expect(wrapper.find(StatusBlock)).toHaveLength(4); - - const completed = wrapper.findWhere((b) => b.prop('completed')); - expect(completed).toHaveLength(2); - expect(completed.map((b) => b.prop('code'))).toEqual(['PROFILE', 'ORDERS']); - - const current = wrapper.findWhere((b) => b.prop('current')); - expect(current).toHaveLength(1); - expect(current.prop('code')).toEqual('ORDERS'); - }); - }); -}); - -describe('StatusBlock', () => { - test('complete but not current status block', () => { - const wrapper = shallow( - , - ); - - expect(wrapper.hasClass('ppm_approved')).toEqual(true); - expect(wrapper.hasClass('status_completed')).toEqual(true); - expect(wrapper.hasClass('status_current')).toEqual(false); - }); - - test('complete and current status block', () => { - const wrapper = shallow(); - - expect(wrapper.hasClass('in_progress')).toEqual(true); - expect(wrapper.hasClass('status_completed')).toEqual(true); - expect(wrapper.hasClass('status_current')).toEqual(true); - }); - - test('incomplete status block', () => { - const wrapper = shallow( - , - ); - - expect(wrapper.hasClass('delivered')).toEqual(true); - expect(wrapper.hasClass('status_completed')).toEqual(false); - expect(wrapper.hasClass('status_current')).toEqual(false); - }); -}); diff --git a/src/scenes/PpmLanding/images/ppm-approved.png b/src/scenes/PpmLanding/images/ppm-approved.png deleted file mode 100644 index b6274b55185..00000000000 Binary files a/src/scenes/PpmLanding/images/ppm-approved.png and /dev/null differ diff --git a/src/scenes/PpmLanding/images/ppm-car.svg b/src/scenes/PpmLanding/images/ppm-car.svg deleted file mode 100644 index fe9a259cf0a..00000000000 --- a/src/scenes/PpmLanding/images/ppm-car.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - Car-gray - Created with Sketch. - - - - - - - - - - - \ No newline at end of file diff --git a/src/scenes/PpmLanding/images/ppm-draft.png b/src/scenes/PpmLanding/images/ppm-draft.png deleted file mode 100644 index 5d129995134..00000000000 Binary files a/src/scenes/PpmLanding/images/ppm-draft.png and /dev/null differ diff --git a/src/scenes/PpmLanding/images/ppm-in-progress.png b/src/scenes/PpmLanding/images/ppm-in-progress.png deleted file mode 100644 index 856338d5a2c..00000000000 Binary files a/src/scenes/PpmLanding/images/ppm-in-progress.png and /dev/null differ diff --git a/src/scenes/PpmLanding/images/ppm-submitted.png b/src/scenes/PpmLanding/images/ppm-submitted.png deleted file mode 100644 index 3bdf0263a99..00000000000 Binary files a/src/scenes/PpmLanding/images/ppm-submitted.png and /dev/null differ diff --git a/src/shared/Entities/modules/moveDocuments.js b/src/shared/Entities/modules/moveDocuments.js index 7d21e9f1f82..bc77082d8bf 100644 --- a/src/shared/Entities/modules/moveDocuments.js +++ b/src/shared/Entities/modules/moveDocuments.js @@ -6,8 +6,6 @@ import { ADD_ENTITIES, addEntities } from '../actions'; import { WEIGHT_TICKET_SET_TYPE, MOVE_DOC_TYPE, MOVE_DOC_STATUS } from '../../constants'; import { getClient, checkResponse } from 'shared/Swagger/api'; -import { swaggerRequest } from 'shared/Swagger/request'; -import * as ReduxHelpers from 'shared/ReduxHelpers'; export const STATE_KEY = 'moveDocuments'; @@ -25,11 +23,6 @@ export default function reducer(state = {}, action) { } } -const deleteMoveDocumentType = 'DELETE_MOVE_DOCUMENT'; -const deleteMoveDocumentLabel = `MoveDocument.deleteMoveDocument`; - -export const DELETE_MOVE_DOCUMENT = ReduxHelpers.generateAsyncActionTypes(deleteMoveDocumentType); - // MoveDocument filter functions const onlyPending = ({ status }) => ![MOVE_DOC_STATUS.OK, MOVE_DOC_STATUS.EXCLUDE].includes(status); const onlyOKed = ({ status }) => status === MOVE_DOC_STATUS.OK; @@ -98,12 +91,6 @@ export const updateMoveDocument = (moveId, moveDocumentId, payload) => { }; }; -export function deleteMoveDocument(moveDocumentId, label = deleteMoveDocumentLabel) { - const schemaKey = 'moveDocuments'; - const deleteId = moveDocumentId; - return swaggerRequest(getClient, 'move_docs.deleteMoveDocument', { moveDocumentId }, { label, schemaKey, deleteId }); -} - // Selectors export const selectMoveDocument = (state, id) => { if (!id) { diff --git a/src/shared/Entities/modules/movingExpenseDocuments.js b/src/shared/Entities/modules/movingExpenseDocuments.js index 0c3d52812c2..d99f6a73c82 100644 --- a/src/shared/Entities/modules/movingExpenseDocuments.js +++ b/src/shared/Entities/modules/movingExpenseDocuments.js @@ -1,7 +1,6 @@ -import { includes, get, filter, map } from 'lodash'; -import { denormalize, normalize } from 'normalizr'; +import { includes, get } from 'lodash'; +import { normalize } from 'normalizr'; -import { moveDocuments } from '../schema'; import { addEntities } from '../actions'; import { getClient, checkResponse } from 'shared/Swagger/api'; @@ -51,17 +50,3 @@ export function createMovingExpenseDocument({ return response; }; } - -export const selectPPMCloseoutDocumentsForMove = ( - state, - id, - selectedDocumentTypes = ['EXPENSE', 'WEIGHT_TICKET_SET'], -) => { - if (!id) { - return []; - } - const movingExpenseDocs = filter(state.entities.moveDocuments, (doc) => { - return doc.move_id === id && selectedDocumentTypes.includes(doc.move_document_type); - }); - return denormalize(map(movingExpenseDocs, 'id'), moveDocuments, state.entities); -}; diff --git a/src/shared/ProgressTimeline/index.css b/src/shared/ProgressTimeline/index.css deleted file mode 100644 index 1895a0d073b..00000000000 --- a/src/shared/ProgressTimeline/index.css +++ /dev/null @@ -1,91 +0,0 @@ -.progress-timeline { - text-align: center; - color: #adadad; - font-size: 1.2rem; -} - -.progress-timeline .step { - position: relative; - width: 80px; - float: left; -} - -.progress-timeline .dot { - display: block; - position: relative; - width: 19px; - height: 19px; - background: white; - border: solid 1px #adadad; - border-radius: 50%; - -moz-border-radius: 50%; - -webkit-border-radius: 50%; - z-index: 10; - left: calc(50% - 10px); -} - -.progress-timeline .step.completed, -.progress-timeline .step.current { - color: #5c8652; -} - -.progress-timeline .step.completed .dot, -.progress-timeline .step.current .dot { - border-color: #5c8652; -} - -.progress-timeline .step.completed .dot:after, -.progress-timeline .step.current .dot:after { - display: block; - content: ' '; - position: absolute; -} - -.progress-timeline .step.completed .dot:after { - border-style: solid; - border-color: #5c8652; - border-width: 0 0 3px 3px; - height: 6px; - width: 9px; - left: 4px; - top: 5px; - transform: rotate(-45deg); -} - -.progress-timeline .step.current .dot:after { - background: #5c8652; - border-radius: 10px; - height: 9px; - width: 9px; - left: 4px; - top: 4px; -} - -.progress-timeline .step:after { - display: block; - content: ' '; - width: 100%; - height: 1px; - background: #adadad; - position: absolute; - right: 50%; - top: 9px; - z-index: 1; -} - -.progress-timeline .step:first-child:after { - display: none; -} - -.progress-timeline .step.completed:after, -.step.current:after { - background: #5c8652; -} - -.progress-timeline .step.completed:first-child:after { - display: none; -} - -.progress-timeline .name { - margin-top: 0.2em; -} diff --git a/src/shared/ProgressTimeline/index.jsx b/src/shared/ProgressTimeline/index.jsx deleted file mode 100644 index 0fdb1dd5586..00000000000 --- a/src/shared/ProgressTimeline/index.jsx +++ /dev/null @@ -1,35 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; - -import './index.css'; - -export const ProgressTimelineStep = function (props) { - const classes = classNames({ - step: true, - completed: props.completed, - current: props.current, - }); - - return ( -
-
-
{props.name}
-
- ); -}; - -ProgressTimelineStep.propTypes = { - completed: PropTypes.bool, - current: PropTypes.bool, -}; - -// ProgressTimeline renders a subway-map-style timeline. Use ProgressTimelineStep -// components as children to declaritively define the "stops" and their status. -export const ProgressTimeline = function (props) { - return
{props.children}
; -}; - -ProgressTimeline.propTypes = { - children: PropTypes.arrayOf(ProgressTimelineStep).isRequired, -}; diff --git a/src/shared/constants.js b/src/shared/constants.js index 353988f9ba1..f59fb6240b1 100644 --- a/src/shared/constants.js +++ b/src/shared/constants.js @@ -11,7 +11,6 @@ export const gitBranch = process.env.REACT_APP_GIT_BRANCH || 'unknown'; export const gitSha = process.env.REACT_APP_GIT_COMMIT || 'unknown'; export const NULL_UUID = '00000000-0000-0000-0000-000000000000'; -export const ppmInfoPacket = '/downloads/ppm_info_sheet.pdf'; export const hostname = window && window.location && window.location.hostname; export const isMilmoveSite = hostname.startsWith('my') || hostname.startsWith('mil') || ''; diff --git a/src/store/entities/actions.js b/src/store/entities/actions.js index a1241932760..fc09b9c44e1 100644 --- a/src/store/entities/actions.js +++ b/src/store/entities/actions.js @@ -4,10 +4,6 @@ export const UPDATE_MOVE = 'UPDATE_MOVE'; export const UPDATE_MTO_SHIPMENT = 'UPDATE_MTO_SHIPMENT'; export const UPDATE_MTO_SHIPMENTS = 'UPDATE_MTO_SHIPMENTS'; export const UPDATE_ORDERS = 'UPDATE_ORDERS'; -export const UPDATE_PPMS = 'UPDATE_PPMS'; -export const UPDATE_PPM = 'UPDATE_PPM'; -export const UPDATE_PPM_ESTIMATE = 'UPDATE_PPM_ESTIMATE'; -export const UPDATE_PPM_SIT_ESTIMATE = 'UPDATE_PPM_SIT_ESTIMATE'; export const UPDATE_OKTA_USER_STATE = 'SET_OKTA_USER'; export const updateOktaUserState = (oktaUser) => ({ @@ -44,23 +40,3 @@ export const updateOrders = (payload) => ({ type: UPDATE_ORDERS, payload, }); - -export const updatePPMs = (payload) => ({ - type: UPDATE_PPMS, - payload, -}); - -export const updatePPM = (payload) => ({ - type: UPDATE_PPM, - payload, -}); - -export const updatePPMEstimate = (payload) => ({ - type: UPDATE_PPM_ESTIMATE, - payload, -}); - -export const updatePPMSitEstimate = (payload) => ({ - type: UPDATE_PPM_SIT_ESTIMATE, - payload, -}); diff --git a/src/store/entities/selectors.js b/src/store/entities/selectors.js index f4bbc4a291d..fa12c58ed03 100644 --- a/src/store/entities/selectors.js +++ b/src/store/entities/selectors.js @@ -234,18 +234,10 @@ export const selectHasCurrentPPM = (state) => { return !!selectCurrentPPM(state); }; -export function selectPPMEstimateRange(state) { - return state.entities?.ppmEstimateRanges?.undefined || null; -} - export function selectPPMSitEstimate(state) { return state.entities?.ppmSitEstimate?.undefined?.estimate || null; } -export function selectReimbursementById(state, reimbursementId) { - return state.entities?.reimbursements?.[`${reimbursementId}`] || null; -} - export const selectWeightAllotmentsForLoggedInUser = createSelector( selectServiceMemberFromLoggedInUser, selectCurrentOrders, diff --git a/src/store/entities/selectors.test.js b/src/store/entities/selectors.test.js index 8e3335d3e17..977f8d3ab25 100644 --- a/src/store/entities/selectors.test.js +++ b/src/store/entities/selectors.test.js @@ -12,9 +12,7 @@ import { selectCurrentMove, selectCurrentPPM, selectPPMForMove, - selectPPMEstimateRange, selectPPMSitEstimate, - selectReimbursementById, selectWeightAllotmentsForLoggedInUser, selectWeightTicketAndIndexById, } from './selectors'; @@ -1537,33 +1535,6 @@ describe('selectCurrentPPM', () => { }); }); -describe('selectPPMEstimateRange', () => { - it('returns the only PPM estimate range stored in entities', () => { - const testState = { - entities: { - ppmEstimateRanges: { - undefined: { - range_min: 1000, - range_max: 2400, - }, - }, - }, - }; - - expect(selectPPMEstimateRange(testState)).toEqual(testState.entities.ppmEstimateRanges.undefined); - }); - - it('returns null if there is no PPM estimate range in entities', () => { - const testState = { - entities: { - ppmEstimateRanges: {}, - }, - }; - - expect(selectPPMEstimateRange(testState)).toEqual(null); - }); -}); - describe('selectPPMSitEstimate', () => { it('returns the only PPM SIT estimate stored in entities', () => { const testState = { @@ -1590,34 +1561,6 @@ describe('selectPPMSitEstimate', () => { }); }); -describe('selectReimbursementById', () => { - it('returns the only PPM SIT estimate stored in entities', () => { - const testState = { - entities: { - reimbursements: { - testReimbursement123: { - id: 'testReimbursement123', - }, - }, - }, - }; - - expect(selectReimbursementById(testState, 'testReimbursement123')).toEqual( - testState.entities.reimbursements.testReimbursement123, - ); - }); - - it('returns null if there is no reimbursement in entities', () => { - const testState = { - entities: { - ppmSitEstimate: {}, - }, - }; - - expect(selectReimbursementById(testState, 'testReimbursement123')).toEqual(null); - }); -}); - describe('selectWeightAllotmentsForLoggedInUser', () => { describe('when I have dependents', () => { describe('when my spouse has pro gear', () => { diff --git a/src/store/onboarding/selectors.js b/src/store/onboarding/selectors.js index 463f5fbc894..a677ee0c773 100644 --- a/src/store/onboarding/selectors.js +++ b/src/store/onboarding/selectors.js @@ -2,6 +2,4 @@ export const selectConusStatus = (state) => { return state.onboarding.conusStatus; }; -export function selectPPMEstimateError(state) { - return state.onboarding.ppmEstimateError || null; -} +export default selectConusStatus; diff --git a/src/stories/statusTimeLine.stories.jsx b/src/stories/statusTimeLine.stories.jsx deleted file mode 100644 index 9a63eca80a6..00000000000 --- a/src/stories/statusTimeLine.stories.jsx +++ /dev/null @@ -1,56 +0,0 @@ -import React from 'react'; -import moment from 'moment'; -import { boolean, text, date } from '@storybook/addon-knobs'; - -import { StatusTimeline } from '../scenes/PpmLanding/StatusTimeline'; - -const StatusTimelineCodes = { - Submitted: 'SUBMITTED', - PpmApproved: 'PPM_APPROVED', - InProgress: 'IN_PROGRESS', - PaymentRequested: 'PAYMENT_REQUESTED', - PaymentReviewed: 'PAYMENT_REVIEWED', -}; - -export default { - title: 'Customer Components/StatusTimeLine', - decorators: [(storyFn) =>
{storyFn()}
], -}; - -export const Basic = () => ( - -); diff --git a/src/utils/incentives.js b/src/utils/incentives.js index 5ce8c43547f..5e1ccf49da0 100644 --- a/src/utils/incentives.js +++ b/src/utils/incentives.js @@ -1,16 +1,8 @@ import { PPM_MAX_ADVANCE_RATIO } from 'constants/shipments'; -import { formatCentsTruncateWhole, formatCentsRange, convertCentsToWholeDollarsRoundedDown } from 'utils/formatters'; +import { formatCentsTruncateWhole, convertCentsToWholeDollarsRoundedDown } from 'utils/formatters'; export const hasShortHaulError = (error) => error?.statusCode === 409; -export const getIncentiveRange = (ppm, estimate) => { - let range = formatCentsRange(ppm?.incentive_estimate_min, ppm?.incentive_estimate_max); - - if (!range) range = formatCentsRange(estimate?.range_min, estimate?.range_max); - - return range || ''; -}; - // Calculates the max advance based on the incentive (in cents). Rounds down and returns a cent value as a number. export const calculateMaxAdvance = (incentive) => { return Math.floor(incentive * PPM_MAX_ADVANCE_RATIO); diff --git a/src/utils/incentives.test.js b/src/utils/incentives.test.js index 20c00ea4c62..bc2793ef821 100644 --- a/src/utils/incentives.test.js +++ b/src/utils/incentives.test.js @@ -1,9 +1,4 @@ -import { - hasShortHaulError, - getIncentiveRange, - calculateMaxAdvance, - calculateMaxAdvanceAndFormatAdvanceAndIncentive, -} from './incentives'; +import { hasShortHaulError, calculateMaxAdvance, calculateMaxAdvanceAndFormatAdvanceAndIncentive } from './incentives'; describe('hasShortHaulError', () => { it('should return true for 409 - move under 50 miles', () => { @@ -17,40 +12,6 @@ describe('hasShortHaulError', () => { }); }); -describe('getIncentiveRange', () => { - it('should return the formatted range from the PPM if the PPM values exist', () => { - expect( - getIncentiveRange( - { - incentive_estimate_min: 1000, - incentive_estimate_max: 2400, - }, - { range_min: 1400, range_max: 2300 }, - ), - ).toBe('$10.00 - 24.00'); - }); - - it('should return the formatted range from the estimate if the PPM values do not exist', () => { - expect(getIncentiveRange({}, { range_min: 1400, range_max: 2300 })).toBe('$14.00 - 23.00'); - }); - - it('should return an empty string if no values exist', () => { - expect(getIncentiveRange({}, {})).toBe(''); - expect( - getIncentiveRange( - { - incentive_estimate_max: '', - incentive_estimate_min: null, - }, - { - range_min: 0, - range_max: undefined, - }, - ), - ).toBe(''); - }); -}); - describe('calculateMaxAdvance', () => { it.each([ [100000, 60000],