Destination type:
diff --git a/src/components/Table/SearchResultsTable.jsx b/src/components/Table/SearchResultsTable.jsx
index 78a0daf5180..f36fc385faa 100644
--- a/src/components/Table/SearchResultsTable.jsx
+++ b/src/components/Table/SearchResultsTable.jsx
@@ -13,12 +13,7 @@ import DateSelectFilter from 'components/Table/Filters/DateSelectFilter';
import LoadingPlaceholder from 'shared/LoadingPlaceholder';
import SomethingWentWrong from 'shared/SomethingWentWrong';
import TextBoxFilter from 'components/Table/Filters/TextBoxFilter';
-import {
- BRANCH_OPTIONS_WITH_MARINE_CORPS,
- MOVE_STATUS_LABELS,
- SEARCH_QUEUE_STATUS_FILTER_OPTIONS,
- SortShape,
-} from 'constants/queues';
+import { BRANCH_OPTIONS, MOVE_STATUS_LABELS, SEARCH_QUEUE_STATUS_FILTER_OPTIONS, SortShape } from 'constants/queues';
import { DATE_FORMAT_STRING } from 'shared/constants';
import { formatDateFromIso, serviceMemberAgencyLabel } from 'utils/formatters';
import MultiSelectCheckBoxFilter from 'components/Table/Filters/MultiSelectCheckBoxFilter';
@@ -107,7 +102,7 @@ const moveSearchColumns = (moveLockFlag, handleEditProfileClick) => [
isFilterable: true,
Filter: (props) => (
// eslint-disable-next-line react/jsx-props-no-spreading
-
+
),
},
),
diff --git a/src/components/Table/TableCSVExportButton.jsx b/src/components/Table/TableCSVExportButton.jsx
index 813f5b7a50d..5d3180ede70 100644
--- a/src/components/Table/TableCSVExportButton.jsx
+++ b/src/components/Table/TableCSVExportButton.jsx
@@ -1,10 +1,12 @@
-import React, { useState, useRef } from 'react';
+import React, { useState, useRef, useContext } from 'react';
import { CSVLink } from 'react-csv';
-import { Link } from '@trussworks/react-uswds';
+import { Button } from '@trussworks/react-uswds';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import moment from 'moment';
import PropTypes from 'prop-types';
+import SelectedGblocContext from 'components/Office/GblocSwitcher/SelectedGblocContext';
+
const TableCSVExportButton = ({
labelText,
filePrefix,
@@ -16,12 +18,16 @@ const TableCSVExportButton = ({
paramSort,
paramFilters,
className,
+ isHeadquartersUser,
}) => {
const [isLoading, setIsLoading] = useState(false);
const [csvRows, setCsvRows] = useState([]);
const csvLinkRef = useRef(null);
const { id: sortColumn, desc: sortOrder } = paramSort.length ? paramSort[0] : {};
+ const gblocContext = useContext(SelectedGblocContext);
+ const { selectedGbloc } = isHeadquartersUser && gblocContext ? gblocContext : { selectedGbloc: undefined };
+
const formatDataForExport = (data, columns = tableColumns) => {
const formattedData = [];
data.forEach((row) => {
@@ -50,6 +56,7 @@ const TableCSVExportButton = ({
order: sortOrder ? 'desc' : 'asc',
filters: paramFilters,
currentPageSize: totalCount,
+ viewAsGBLOC: selectedGbloc,
});
const formattedData = formatDataForExport(response[queueFetcherKey]);
@@ -61,15 +68,23 @@ const TableCSVExportButton = ({
return (
-
+
@@ -96,6 +111,8 @@ TableCSVExportButton.propTypes = {
paramSort: PropTypes.array,
// paramSort is the filter columns and values currently applied to the queue
paramFilters: PropTypes.array,
+ // isHeadquartersUser identifies if the active role is a headquarters user to allow switching GBLOCs
+ isHeadquartersUser: PropTypes.bool,
};
TableCSVExportButton.defaultProps = {
@@ -104,6 +121,7 @@ TableCSVExportButton.defaultProps = {
hiddenColumns: [],
paramSort: [],
paramFilters: [],
+ isHeadquartersUser: false,
};
export default TableCSVExportButton;
diff --git a/src/components/Table/TableCSVExportButton.test.jsx b/src/components/Table/TableCSVExportButton.test.jsx
index a3847752000..da8b2102f13 100644
--- a/src/components/Table/TableCSVExportButton.test.jsx
+++ b/src/components/Table/TableCSVExportButton.test.jsx
@@ -55,6 +55,11 @@ const paymentRequestsResponse = {
],
};
+const paymentRequestsNoResultsResponse = {
+ page: 1,
+ perPage: 10,
+};
+
const paymentRequestColumns = [
{
Header: ' ',
@@ -129,6 +134,9 @@ const paymentRequestColumns = [
jest.mock('services/ghcApi', () => ({
getPaymentRequestsQueue: jest.fn().mockImplementation(() => Promise.resolve(paymentRequestsResponse)),
+ getPaymentRequestsNoResultsQueue: jest
+ .fn()
+ .mockImplementation(() => Promise.resolve(paymentRequestsNoResultsResponse)),
}));
describe('TableCSVExportButton', () => {
@@ -155,4 +163,22 @@ describe('TableCSVExportButton', () => {
expect(getPaymentRequestsQueue).toBeCalled();
});
+
+ const noResultsProps = {
+ tableColumns: paymentRequestColumns,
+ queueFetcher: () => Promise.resolve(paymentRequestsNoResultsResponse),
+ queueFetcherKey: 'queuePaymentRequests',
+ totalCount: 0,
+ };
+
+ it('is diabled when there is nothing to export', () => {
+ act(() => {
+ const wrapper = mount();
+ const exportButton = wrapper.find('span[data-test-id="csv-export-btn-text"]');
+ exportButton.simulate('click');
+ wrapper.update();
+ });
+
+ expect(getPaymentRequestsQueue).toBeCalled();
+ });
});
diff --git a/src/components/Table/TableQueue.jsx b/src/components/Table/TableQueue.jsx
index 5182fed428d..d09605914c4 100644
--- a/src/components/Table/TableQueue.jsx
+++ b/src/components/Table/TableQueue.jsx
@@ -48,6 +48,7 @@ const TableQueue = ({
csvExportQueueFetcher,
csvExportQueueFetcherKey,
sessionStorageKey,
+ isHeadquartersUser,
}) => {
const [isPageReload, setIsPageReload] = useState(true);
useEffect(() => {
@@ -87,7 +88,7 @@ const TableQueue = ({
const { id, desc } = paramSort.length ? paramSort[0] : {};
const gblocContext = useContext(SelectedGblocContext);
- const { selectedGbloc } = gblocContext || { selectedGbloc: undefined };
+ const { selectedGbloc } = isHeadquartersUser && gblocContext ? gblocContext : { selectedGbloc: undefined };
const multiSelectValueDelimiter = ',';
@@ -312,6 +313,7 @@ const TableQueue = ({
totalCount={totalCount}
paramSort={paramSort}
paramFilters={paramFilters}
+ isHeadquartersUser={isHeadquartersUser}
/>
)}
@@ -381,6 +383,8 @@ TableQueue.propTypes = {
csvExportQueueFetcherKey: PropTypes.string,
// session storage key to store search filters
sessionStorageKey: PropTypes.string,
+ // isHeadquartersUser identifies if the active role is a headquarters user to allow switching GBLOCs
+ isHeadquartersUser: PropTypes.bool,
};
TableQueue.defaultProps = {
@@ -399,5 +403,6 @@ TableQueue.defaultProps = {
csvExportQueueFetcher: null,
csvExportQueueFetcherKey: null,
sessionStorageKey: 'default',
+ isHeadquartersUser: false,
};
export default TableQueue;
diff --git a/src/constants/queues.js b/src/constants/queues.js
index 87d23e1ed04..17a6e9d4066 100644
--- a/src/constants/queues.js
+++ b/src/constants/queues.js
@@ -52,15 +52,6 @@ export const BRANCH_OPTIONS = [
{ value: 'AIR_FORCE', label: 'Air Force' },
{ value: 'COAST_GUARD', label: 'Coast Guard' },
{ value: 'SPACE_FORCE', label: 'Space Force' },
-];
-
-export const BRANCH_OPTIONS_WITH_MARINE_CORPS = [
- { value: '', label: 'All' },
- { value: 'ARMY', label: 'Army' },
- { value: 'NAVY', label: 'Navy' },
- { value: 'AIR_FORCE', label: 'Air Force' },
- { value: 'COAST_GUARD', label: 'Coast Guard' },
- { value: 'SPACE_FORCE', label: 'Space Force' },
{ value: 'MARINES', label: 'Marine Corps' },
];
diff --git a/src/pages/MyMove/Profile/ContactInfo.jsx b/src/pages/MyMove/Profile/ContactInfo.jsx
index 2b25f520797..298789034a0 100644
--- a/src/pages/MyMove/Profile/ContactInfo.jsx
+++ b/src/pages/MyMove/Profile/ContactInfo.jsx
@@ -37,13 +37,13 @@ export const ContactInfo = ({ serviceMember, updateServiceMember, userEmail }) =
const payload = {
id: serviceMember.id,
telephone: values?.telephone,
- secondary_telephone: values?.secondary_telephone,
+ secondary_telephone: values?.secondary_telephone || '',
personal_email: values?.personal_email,
phone_is_preferred: values?.phone_is_preferred,
email_is_preferred: values?.email_is_preferred,
};
- if (!payload.secondary_telephone) {
- delete payload.secondary_telephone;
+ if (!payload.secondary_telephone || payload.secondary_telephone === '') {
+ payload.secondary_telephone = '';
}
return patchServiceMember(payload)
diff --git a/src/pages/MyMove/Profile/EditContactInfo.jsx b/src/pages/MyMove/Profile/EditContactInfo.jsx
index aa27b30eae0..898031e46b8 100644
--- a/src/pages/MyMove/Profile/EditContactInfo.jsx
+++ b/src/pages/MyMove/Profile/EditContactInfo.jsx
@@ -73,9 +73,7 @@ export const EditContactInfo = ({
backup_mailing_address: values[backupAddressName.toString()],
};
- if (values?.secondary_telephone) {
- serviceMemberPayload.secondary_telephone = values?.secondary_telephone;
- }
+ serviceMemberPayload.secondary_telephone = values?.secondary_telephone;
const backupContactPayload = {
id: currentBackupContacts[0].id,
diff --git a/src/pages/Office/CustomerOnboarding/CreateCustomerForm.jsx b/src/pages/Office/CustomerOnboarding/CreateCustomerForm.jsx
index b8a6ed6a6fa..69354c506e6 100644
--- a/src/pages/Office/CustomerOnboarding/CreateCustomerForm.jsx
+++ b/src/pages/Office/CustomerOnboarding/CreateCustomerForm.jsx
@@ -27,11 +27,14 @@ import { setFlashMessage as setFlashMessageAction } from 'store/flash/actions';
import { elevatedPrivilegeTypes } from 'constants/userPrivileges';
import { isBooleanFlagEnabled } from 'utils/featureFlags';
import departmentIndicators from 'constants/departmentIndicators';
+import { generateUniqueDodid, generateUniqueEmplid } from 'utils/customer';
+import Hint from 'components/Hint';
export const CreateCustomerForm = ({ userPrivileges, setFlashMessage }) => {
const [serverError, setServerError] = useState(null);
const [showEmplid, setShowEmplid] = useState(false);
const [isSafetyMove, setIsSafetyMove] = useState(false);
+ const [showSafetyMoveHint, setShowSafetyMoveHint] = useState(false);
const navigate = useNavigate();
const branchOptions = dropdownInputOptions(SERVICE_MEMBER_AGENCY_LABELS);
@@ -42,6 +45,9 @@ export const CreateCustomerForm = ({ userPrivileges, setFlashMessage }) => {
const [isSafetyMoveFF, setSafetyMoveFF] = useState(false);
+ const uniqueDodid = generateUniqueDodid();
+ const uniqueEmplid = generateUniqueEmplid();
+
useEffect(() => {
isBooleanFlagEnabled('safety_move')?.then((enabled) => {
setSafetyMoveFF(enabled);
@@ -55,6 +61,7 @@ export const CreateCustomerForm = ({ userPrivileges, setFlashMessage }) => {
const initialValues = {
affiliation: '',
edipi: '',
+ emplid: '',
first_name: '',
middle_name: '',
last_name: '',
@@ -87,7 +94,7 @@ export const CreateCustomerForm = ({ userPrivileges, setFlashMessage }) => {
},
create_okta_account: '',
cac_user: '',
- is_safety_move: false,
+ is_safety_move: 'false',
};
const handleBack = () => {
@@ -144,10 +151,26 @@ export const CreateCustomerForm = ({ userPrivileges, setFlashMessage }) => {
const validationSchema = Yup.object().shape({
affiliation: Yup.mixed().oneOf(Object.keys(SERVICE_MEMBER_AGENCY_LABELS)).required('Required'),
- edipi: Yup.string().matches(/[0-9]{10}/, 'Enter a 10-digit DOD ID number'),
- emplid: Yup.string()
- .notRequired()
- .matches(/[0-9]{7}/, 'Enter a 7-digit EMPLID number'),
+ // All branches require an EDIPI unless it is a safety move
+ // where a fake DoD ID may be used
+ edipi:
+ !isSafetyMove &&
+ Yup.string()
+ .matches(/^(SM[0-9]{8}|[0-9]{10})$/, 'Enter a 10-digit DoD ID number')
+ .required('Required'),
+ // Only the coast guard requires both EDIPI and EMPLID
+ // unless it is a safety move
+ emplid:
+ !isSafetyMove &&
+ showEmplid &&
+ Yup.string().when('affiliation', {
+ is: (affiliationValue) => affiliationValue === departmentIndicators.COAST_GUARD,
+ then: () =>
+ Yup.string()
+ .matches(/^(SM[0-9]{5}|[0-9]{7})$/, 'Enter a 7-digit EMPLID number')
+ .required(`EMPLID is required for the Coast Guard`),
+ otherwise: Yup.string().notRequired(),
+ }),
first_name: Yup.string().required('Required'),
middle_name: Yup.string(),
last_name: Yup.string().required('Required'),
@@ -193,11 +216,10 @@ export const CreateCustomerForm = ({ userPrivileges, setFlashMessage }) => {
const { value } = e.target;
if (value === 'true') {
setIsSafetyMove(true);
- // clear out DoDID, emplid, and OKTA fields
+ setShowSafetyMoveHint(true);
setValues({
...values,
- edipi: '',
- emplid: '',
+ affiliation: '',
create_okta_account: '',
cac_user: 'true',
is_safety_move: 'true',
@@ -206,15 +228,47 @@ export const CreateCustomerForm = ({ userPrivileges, setFlashMessage }) => {
setIsSafetyMove(false);
setValues({
...values,
+ affiliation: '',
+ edipi: '',
+ emplid: '',
is_safety_move: 'false',
});
}
};
const handleBranchChange = (e) => {
- if (e.target.value === departmentIndicators.COAST_GUARD) {
+ setShowSafetyMoveHint(false);
+ if (e.target.value === departmentIndicators.COAST_GUARD && isSafetyMove) {
setShowEmplid(true);
+ setValues({
+ ...values,
+ affiliation: e.target.value,
+ edipi: uniqueDodid,
+ emplid: uniqueEmplid,
+ });
+ } else if (e.target.value === departmentIndicators.COAST_GUARD && !isSafetyMove) {
+ setShowEmplid(true);
+ setValues({
+ ...values,
+ affiliation: e.target.value,
+ edipi: '',
+ emplid: '',
+ });
+ } else if (e.target.value !== departmentIndicators.COAST_GUARD && isSafetyMove) {
+ setShowEmplid(false);
+ setValues({
+ ...values,
+ affiliation: e.target.value,
+ edipi: uniqueDodid,
+ emplid: '',
+ });
} else {
setShowEmplid(false);
+ setValues({
+ ...values,
+ affiliation: e.target.value,
+ edipi: '',
+ emplid: '',
+ });
}
};
return (
@@ -234,6 +288,7 @@ export const CreateCustomerForm = ({ userPrivileges, setFlashMessage }) => {
value="true"
data-testid="is-safety-move-yes"
onChange={handleIsSafetyMove}
+ checked={values.is_safety_move === 'true'}
/>