Skip to content

Commit

Permalink
GI CT Search by program (#34150)
Browse files Browse the repository at this point in the history
* Wip

* Program search required fields

* Search by program components

* Testing

* Testing

* Testing, require distance field

* Testing and focusLocationInput on location modal close

* Distance dropdown validation message, testing

* Toggle
  • Loading branch information
GovCIOLiz authored Jan 17, 2025
1 parent b4e43a9 commit f3bc3a9
Show file tree
Hide file tree
Showing 14 changed files with 444 additions and 102 deletions.
26 changes: 18 additions & 8 deletions src/applications/gi/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -507,23 +507,31 @@ export function fetchSearchByLocationCoords(
distance,
filters,
version,
description,
) {
const [longitude, latitude] = coordinates;

const params = {
latitude,
longitude,
distance,
...rubyifyKeys(buildSearchFilters(filters)),
};
// If description - search by program, else search by location w/ filters
const params = description
? {
latitude,
longitude,
distance,
description,
}
: {
latitude,
longitude,
distance,
...rubyifyKeys(filters && buildSearchFilters(filters)),
};
if (version) {
params.version = version;
}
const url = appendQuery(`${api.url}/institutions/search`, params);
return dispatch => {
dispatch({
type: SEARCH_STARTED,
payload: { location, latitude, longitude, distance },
payload: { location, latitude, longitude, distance, description },
});

return fetch(url, api.settings)
Expand Down Expand Up @@ -563,6 +571,7 @@ export function fetchSearchByLocationResults(
distance,
filters,
version,
description,
) {
// Prevent empty search request to Mapbox, which would result in error, and
// clear results list to respond with message of no facilities found.
Expand Down Expand Up @@ -593,6 +602,7 @@ export function fetchSearchByLocationResults(
distance,
filters,
version,
description,
),
);
})
Expand Down
1 change: 1 addition & 0 deletions src/applications/gi/tests/actions/index.unit.spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,7 @@ describe('actionCreators', () => {
10,
{ filter1: 'value1', excludedSchoolTypes: 'value2' },
'version',
null,
)(mockDispatch)
.then(() => {
expect(
Expand Down
75 changes: 0 additions & 75 deletions src/applications/gi/updated-gi/components/SearchByProgram.jsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';

export const UseMyLocation = ({ geolocationInProgress, handleLocateUser }) => {
return (
<>
{geolocationInProgress ? (
<div className="vads-u-display--flex vads-u-align-items--center vads-u-color--primary">
<va-icon size={3} icon="autorenew" aria-hidden="true" />
<span aria-live="assertive">Finding your location...</span>
</div>
) : (
<>
{/* eslint-disable-next-line @department-of-veterans-affairs/prefer-button-component */}
<button
type="button"
className="vads-u-line-height--3 vads-u-padding--0 vads-u-margin--0 vads-u-color--primary vads-u-background-color--white vads-u-font-weight--normal"
onClick={handleLocateUser}
>
<va-icon icon="near_me" size={3} />
Use my location
</button>
</>
)}
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import { useDispatch } from 'react-redux';
import { VaModal } from '@department-of-veterans-affairs/component-library/dist/react-bindings';
import { clearGeocodeError } from '../../../actions';

export const UseMyLocationModal = ({ geocodeError, focusLocationInput }) => {
const dispatch = useDispatch();

return (
<VaModal
modalTitle={
geocodeError === 1
? 'We need to use your location'
: "We couldn't locate you"
}
onCloseEvent={() => {
focusLocationInput();
dispatch(clearGeocodeError());
}}
status="warning"
visible={geocodeError > 0}
>
<p>
{geocodeError === 1
? 'Please enable location sharing in your browser to use this feature.'
: 'Sorry, something went wrong when trying to find your location. Please make sure location sharing is enabled and try again.'}
</p>
</VaModal>
);
};
40 changes: 23 additions & 17 deletions src/applications/gi/updated-gi/containers/SchoolsAndEmployers.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import React, { useState } from 'react';
import { Tab, TabList, TabPanel, Tabs } from 'react-tabs';
import SearchByName from '../components/SearchByName';
import SearchByProgram from '../components/SearchByProgram';
import SearchByName from './SearchByName';
import SearchByProgram from './SearchByProgram';

const SchoolAndEmployers = () => {
const [currentTab, setCurrentTab] = useState(0);
const tabPanelClassList =
'vads-u-border-bottom--1px vads-u-border-left--1px vads-u-border-right--1px vads-u-border-color--primary medium-screen:vads-u-padding--4 mobile:vads-u-padding--2';
const baseTabClassList =
'vads-l-col vads-u-display--flex vads-u-justify-content--center vads-u-align-items--center vads-u-margin-bottom--0 vads-u-text-align--center vads-u-border-top--5px vads-u-border-left--1px vads-u-border-right--1px';
'vads-u-font-family--serif vads-u-font-size--h3 vads-l-col vads-u-display--flex vads-u-justify-content--center vads-u-align-items--center vads-u-margin-bottom--0 vads-u-text-align--center vads-u-border-top--5px vads-u-border-left--1px vads-u-border-right--1px';
const inactiveTabClassList = `${baseTabClassList} vads-u-background-color--base-lightest vads-u-border-color--base-lightest vads-u-border-bottom--1px`;
const activeTabClassList = `${baseTabClassList} vads-u-border-color--primary`;
const inactiveTabText = 'vads-u-color--gray-dark vads-u-margin--0';
Expand All @@ -27,30 +27,36 @@ const SchoolAndEmployers = () => {
style={{ listStyle: 'none', cursor: 'pointer' }}
>
<Tab
tabIndex="0"
className={
currentTab === 0 ? activeTabClassList : inactiveTabClassList
}
>
<h3
className={
currentTab === 1 ? inactiveTabText : 'vads-u-margin--0'
}
>
Search by name
</h3>
<strong>
<span
className={
currentTab === 1 ? inactiveTabText : 'vads-u-margin--0'
}
>
Search by name
</span>
</strong>
</Tab>
<Tab
tabIndex="0"
className={
currentTab === 1 ? activeTabClassList : inactiveTabClassList
}
>
<h3
className={
currentTab === 0 ? inactiveTabText : 'vads-u-margin--0'
}
>
Search by program
</h3>
<strong>
<span
className={
currentTab === 0 ? inactiveTabText : 'vads-u-margin--0'
}
>
Search by program
</span>
</strong>
</Tab>
</TabList>
<TabPanel className={currentTab === 0 ? tabPanelClassList : null}>
Expand Down
125 changes: 125 additions & 0 deletions src/applications/gi/updated-gi/containers/SearchByProgram.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import React, { useEffect, useRef, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import recordEvent from 'platform/monitoring/record-event';
import {
VaButton,
VaSelect,
VaTextInput,
} from '@department-of-veterans-affairs/component-library/dist/react-bindings';
import { geolocateUser, fetchSearchByLocationResults } from '../../actions';
import { UseMyLocation } from '../components/school-and-employers/UseMyLocation';
import { UseMyLocationModal } from '../components/school-and-employers/UseMyLocationModal';

const SearchByProgram = () => {
const locationRef = useRef(null);
const distanceDropdownOptions = [
{ value: '5', label: 'within 5 miles' },
{ value: '15', label: 'within 15 miles' },
{ value: '25', label: 'within 25 miles' },
{ value: '50', label: 'within 50 miles' },
{ value: '75', label: 'within 75 miles' },
];
const dispatch = useDispatch();
const search = useSelector(state => state.search);
const [distance, setDistance] = useState(search.query.distance);
const [location, setLocation] = useState(search.query.location);
const [programName, setProgramName] = useState(null);
const [searchDirty, setSearchDirty] = useState(false);

const focusLocationInput = () => {
locationRef?.current?.shadowRoot?.querySelector('input').focus();
};

const handleLocateUser = e => {
e.preventDefault();
recordEvent({
event: 'map-use-my-location',
});
dispatch(geolocateUser());
};

const handleSearch = () => {
if (!searchDirty) {
setSearchDirty(true);
return;
}
const description = programName;
dispatch(
fetchSearchByLocationResults(location, distance, null, null, description),
);
// Show program results...
};

useEffect(
() => {
const { searchString } = search.query.streetAddress;
if (searchString) {
setLocation(searchString);
focusLocationInput();
}
},
[search],
);

return (
<div className="vads-u-display--flex mobile:vads-u-flex-direction--column medium-screen:vads-u-flex-direction--row vads-u-justify-content--space-between mobile:vads-u-align-items--flex-start medium-screen:vads-u-align-items--flex-end">
<UseMyLocationModal
geocodeError={search.geocodeError}
focusLocationInput={focusLocationInput}
/>
<VaTextInput
className="tablet:vads-u-flex--3 mobile:vads-u-width--full vads-u-margin-right--2p5 mobile:vads-u-margin-top--neg2p5"
name="program-name"
type="text"
label="Name of program"
required
value={programName}
error={
searchDirty && !programName ? 'Please fill in a program name.' : null
}
onInput={e => setProgramName(e.target.value)}
/>
<VaTextInput
ref={locationRef}
className="tablet:vads-u-flex--3 mobile:vads-u-width--full vads-u-margin-right--2p5"
name="program-location"
type="text"
label="City, state, or postal code"
required
value={location}
error={
searchDirty && !location
? 'Please fill in a city, state, or postal code.'
: null
}
onInput={e => setLocation(e.target.value)}
/>
<div className="medium-screen:vads-u-flex--2 tablet:vads-u-flex--auto vads-u-margin-right--2p5 mobile:vads-u-margin-top--2p5">
<UseMyLocation
geolocationInProgress={search.geolocationInProgress}
handleLocateUser={handleLocateUser}
/>
<VaSelect
name="program-distance"
onVaSelect={e => setDistance(e.target.value)}
value={distance}
required
error={searchDirty && !distance ? 'Please select a distance' : null}
>
{distanceDropdownOptions.map(option => (
<option value={option.value} key={option.value}>
{option.label}
</option>
))}
</VaSelect>
</div>
<VaButton
className="vads-u-flex--auto mobile:vads-u-margin-top--2p5"
onClick={handleSearch}
text="Search"
/>
</div>
);
};

export default SearchByProgram;
Loading

0 comments on commit f3bc3a9

Please sign in to comment.