Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MAIN B-18313 Multi-Moves Profile Validation & Create a Move Btn Functionality #12041

Merged
merged 5 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import { Link, useLocation } from 'react-router-dom';

import styles from './ContactInfoDisplay.module.scss';

Expand Down Expand Up @@ -28,11 +28,15 @@ const ContactInfoDisplay = ({
preferredContactMethod = 'Email';
}

const { state } = useLocation();

return (
<div className={styles.contactInfoContainer}>
<div className={styles.contactInfoHeader}>
<h2>Contact info</h2>
<Link to={editURL}>Edit</Link>
<Link to={editURL} state={state}>
Edit
</Link>
</div>

<div className={styles.contactInfoSection}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { string, PropTypes } from 'prop-types';
import { Link } from 'react-router-dom';
import { Link, useLocation } from 'react-router-dom';

import oktaLogo from '../../../../shared/images/okta_logo.png';

Expand All @@ -9,11 +9,13 @@ import oktaInfoDisplayStyles from './OktaInfoDisplay.module.scss';
import descriptionListStyles from 'styles/descriptionList.module.scss';

const OktaInfoDisplay = ({ editURL, oktaUsername, oktaEmail, oktaFirstName, oktaLastName, oktaEdipi }) => {
const { state } = useLocation();

return (
<div className={oktaInfoDisplayStyles.serviceInfoContainer}>
<div className={oktaInfoDisplayStyles.header}>
<img className={oktaInfoDisplayStyles.oktaLogo} src={oktaLogo} alt="Okta logo" />
<Link className={oktaInfoDisplayStyles.oktaEditLink} to={editURL}>
<Link className={oktaInfoDisplayStyles.oktaEditLink} to={editURL} state={state}>
Edit
</Link>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { string, bool } from 'prop-types';
import { Link } from 'react-router-dom';
import { Link, useLocation } from 'react-router-dom';

import serviceInfoDisplayStyles from './ServiceInfoDisplay.module.scss';

Expand All @@ -19,11 +19,17 @@ const ServiceInfoDisplay = ({
editURL,
payGrade,
}) => {
const { state } = useLocation();

return (
<div className={serviceInfoDisplayStyles.serviceInfoContainer}>
<div className={serviceInfoDisplayStyles.header}>
<h2>Service info</h2>
{isEditable && <Link to={editURL}>Edit</Link>}
{isEditable && (
<Link to={editURL} state={state}>
Edit
</Link>
)}
</div>
{!isEditable && showMessage && (
<div className={serviceInfoDisplayStyles.whoToContactContainer}>
Expand Down
64 changes: 58 additions & 6 deletions src/pages/MyMove/Multi-Moves/MultiMovesLandingPage.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React, { useEffect, useState } from 'react';
import { Button } from '@trussworks/react-uswds';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { useNavigate } from 'react-router';
import { connect } from 'react-redux';

import styles from './MultiMovesLandingPage.module.scss';
import MultiMovesMoveHeader from './MultiMovesMoveHeader/MultiMovesMoveHeader';
Expand All @@ -14,16 +16,28 @@ import {
mockMovesNoCurrentOrPreviousMoves,
} from './MultiMovesTestData';

import { detectFlags } from 'utils/featureFlags';
import { generatePageTitle } from 'hooks/custom';
import { milmoveLogger } from 'utils/milmoveLog';
import retryPageLoading from 'utils/retryPageLoading';
import { loadInternalSchema } from 'shared/Swagger/ducks';
import { loadUser } from 'store/auth/actions';
import { initOnboarding } from 'store/onboarding/actions';
import Helper from 'components/Customer/Home/Helper';
import { customerRoutes } from 'constants/routes';
import { withContext } from 'shared/AppContext';
import withRouter from 'utils/routing';
import requireCustomerState from 'containers/requireCustomerState/requireCustomerState';
import {
selectCurrentMove,
selectIsProfileComplete,
selectServiceMemberFromLoggedInUser,
} from 'store/entities/selectors';

const MultiMovesLandingPage = () => {
const [setErrorState] = useState({ hasError: false, error: undefined, info: undefined });
const navigate = useNavigate();

// ! This is just used for testing and viewing different variations of data that MilMove will use
// user can add params of ?moveData=PCS, etc to view different views
let moves;
Expand Down Expand Up @@ -53,7 +67,6 @@ const MultiMovesLandingPage = () => {
break;
}
// ! end of test data

useEffect(() => {
const fetchData = async () => {
try {
Expand Down Expand Up @@ -81,7 +94,22 @@ const MultiMovesLandingPage = () => {
fetchData();
}, [setErrorState]);

return (
const flags = detectFlags(process.env.NODE_ENV, window.location.host, window.location.search);

// handles logic when user clicks "Create a Move" button
// if they have previous moves, they'll need to validate their profile
// if they do not have previous moves, then they don't need to validate
const handleCreateMoveBtnClick = () => {
if (moves.previousMoves.length > 0) {
const profileEditPath = customerRoutes.PROFILE_PATH;
navigate(profileEditPath, { state: { needsToVerifyProfile: true } });
} else {
navigate(customerRoutes.MOVE_HOME_PAGE);
}
};

// ! WILL ONLY SHOW IF MULTIMOVE FLAG IS TRUE
return flags.multiMove ? (
<div>
<div className={styles.homeContainer}>
<header data-testid="customerHeader" className={styles.customerHeader}>
Expand All @@ -91,13 +119,13 @@ const MultiMovesLandingPage = () => {
</header>
<div className={`usa-prose grid-container ${styles['grid-container']}`}>
<Helper title="Welcome to MilMove!" className={styles['helper-paragraph-only']}>
<p>
<p data-testid="welcomeHeader">
We can put information at the top here - potentially important contact info or basic instructions on how
to start a move?
</p>
</Helper>
<div className={styles.centeredContainer}>
<Button className={styles.createMoveBtn}>
<Button className={styles.createMoveBtn} onClick={handleCreateMoveBtnClick} data-testid="createMoveBtn">
<span>Create a Move</span>
<div>
<FontAwesomeIcon icon="plus" />
Expand Down Expand Up @@ -143,7 +171,31 @@ const MultiMovesLandingPage = () => {
</div>
</div>
</div>
);
) : null;
};

export default MultiMovesLandingPage;
MultiMovesLandingPage.defaultProps = {
serviceMember: null,
};

const mapStateToProps = (state) => {
const serviceMember = selectServiceMemberFromLoggedInUser(state);
const move = selectCurrentMove(state) || {};

return {
isProfileComplete: selectIsProfileComplete(state),
serviceMember,
move,
};
};

// in order to avoid setting up proxy server only for storybook, pass in stub function so API requests don't fail
const mergeProps = (stateProps, dispatchProps, ownProps) => ({
...stateProps,
...dispatchProps,
...ownProps,
});

export default withContext(
withRouter(connect(mapStateToProps, mergeProps)(requireCustomerState(MultiMovesLandingPage))),
);
55 changes: 51 additions & 4 deletions src/pages/MyMove/Multi-Moves/MultiMovesLandingPage.test.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { v4 } from 'uuid';

import '@testing-library/jest-dom/extend-expect';

import '@testing-library/jest-dom/extend-expect'; // For additional matchers like toBeInTheDocument
import MultiMovesLandingPage from './MultiMovesLandingPage';

import { MockProviders } from 'testUtils';
import { MOVE_STATUSES } from 'shared/constants';

// Mock external dependencies
jest.mock('utils/featureFlags', () => ({
detectFlags: jest.fn(() => ({ multiMove: true })),
Expand All @@ -21,12 +26,50 @@ jest.mock('shared/Swagger/ducks', () => ({
loadInternalSchema: jest.fn(),
}));

const defaultProps = {
serviceMember: {
id: v4(),
current_location: {
transportation_office: {
name: 'Test Transportation Office Name',
phone_lines: ['555-555-5555'],
},
},
weight_allotment: {
total_weight_self: 8000,
total_weight_self_plus_dependents: 11000,
},
},
showLoggedInUser: jest.fn(),
createServiceMember: jest.fn(),
getSignedCertification: jest.fn(),
mtoShipments: [],
mtoShipment: {},
isLoggedIn: true,
loggedInUserIsLoading: false,
loggedInUserSuccess: true,
isProfileComplete: true,
loadMTOShipments: jest.fn(),
updateShipmentList: jest.fn(),
move: {
id: v4(),
status: MOVE_STATUSES.DRAFT,
},
uploadedOrderDocuments: [],
uploadedAmendedOrderDocuments: [],
};

describe('MultiMovesLandingPage', () => {
it('renders the component with retirement moves', () => {
render(<MultiMovesLandingPage />);
it('renders the component with moves', () => {
render(
<MockProviders>
<MultiMovesLandingPage {...defaultProps} />
</MockProviders>,
);

// Check for specific elements
expect(screen.getByTestId('customerHeader')).toBeInTheDocument();
expect(screen.getByTestId('welcomeHeader')).toBeInTheDocument();
expect(screen.getByText('First Last')).toBeInTheDocument();
expect(screen.getByText('Welcome to MilMove!')).toBeInTheDocument();
expect(screen.getByText('Create a Move')).toBeInTheDocument();
Expand All @@ -37,7 +80,11 @@ describe('MultiMovesLandingPage', () => {
});

it('renders move data correctly', () => {
render(<MultiMovesLandingPage />);
render(
<MockProviders>
<MultiMovesLandingPage />
</MockProviders>,
);

expect(screen.getByTestId('currentMoveHeader')).toBeInTheDocument();
expect(screen.getByTestId('currentMoveContainer')).toBeInTheDocument();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@ import React, { useState } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import classnames from 'classnames';
import { Button } from '@trussworks/react-uswds';
import { useNavigate } from 'react-router';

import MultiMovesMoveInfoList from '../MultiMovesMoveInfoList/MultiMovesMoveInfoList';
import ButtonDropdownMenu from '../../../../components/ButtonDropdownMenu/ButtonDropdownMenu';

import styles from './MultiMovesMoveContainer.module.scss';

import ShipmentContainer from 'components/Office/ShipmentContainer/ShipmentContainer';
import { customerRoutes } from 'constants/routes';

const MultiMovesMoveContainer = ({ moves }) => {
const [expandedMoves, setExpandedMoves] = useState({});
const navigate = useNavigate();

// this expands the moves when the arrow is clicked
const handleExpandClick = (index) => {
Expand Down Expand Up @@ -63,13 +66,24 @@ const MultiMovesMoveContainer = ({ moves }) => {
return 'Shipment';
};

// sends user to the move page when clicking "Go to Move" btn
const handleGoToMoveClick = () => {
navigate(customerRoutes.MOVE_HOME_PAGE);
};

const moveList = moves.map((m, index) => (
<React.Fragment key={index}>
<div className={styles.moveContainer}>
<div className={styles.heading} key={index}>
<h3>#{m.moveCode}</h3>
{m.status !== 'APPROVED' ? (
<Button className={styles.goToMoveBtn} secondary outline>
<Button
data-testid="goToMoveBtn"
className={styles.goToMoveBtn}
secondary
outline
onClick={handleGoToMoveClick}
>
Go to Move
</Button>
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,44 @@ import { mockMovesPCS, mockMovesRetirement, mockMovesSeparation } from '../Multi

import MultiMovesMoveContainer from './MultiMovesMoveContainer';

import { MockProviders } from 'testUtils';

export default {
title: 'Customer Components / MultiMovesContainer',
};

export const PCSCurrentMove = () => <MultiMovesMoveContainer moves={mockMovesPCS.currentMove} />;

export const PCSPreviousMoves = () => <MultiMovesMoveContainer moves={mockMovesPCS.previousMoves} />;

export const RetirementCurrentMove = () => <MultiMovesMoveContainer moves={mockMovesRetirement.currentMove} />;

export const RetirementPreviousMoves = () => <MultiMovesMoveContainer moves={mockMovesRetirement.previousMoves} />;

export const SeparationCurrentMove = () => <MultiMovesMoveContainer moves={mockMovesSeparation.currentMove} />;

export const SeparationPreviousMoves = () => <MultiMovesMoveContainer moves={mockMovesSeparation.previousMoves} />;
export const PCSCurrentMove = () => (
<MockProviders>
<MultiMovesMoveContainer moves={mockMovesPCS.currentMove} />
</MockProviders>
);

export const PCSPreviousMoves = () => (
<MockProviders>
<MultiMovesMoveContainer moves={mockMovesPCS.previousMoves} />
</MockProviders>
);

export const RetirementCurrentMove = () => (
<MockProviders>
<MultiMovesMoveContainer moves={mockMovesRetirement.currentMove} />
</MockProviders>
);

export const RetirementPreviousMoves = () => (
<MockProviders>
<MultiMovesMoveContainer moves={mockMovesRetirement.previousMoves} />
</MockProviders>
);

export const SeparationCurrentMove = () => (
<MockProviders>
<MultiMovesMoveContainer moves={mockMovesSeparation.currentMove} />
</MockProviders>
);

export const SeparationPreviousMoves = () => (
<MockProviders>
<MultiMovesMoveContainer moves={mockMovesSeparation.previousMoves} />
</MockProviders>
);
Loading
Loading