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

Staging #150

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
3 changes: 1 addition & 2 deletions .env.development
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
# development API and keys
REACT_APP_API_HOST='https://apisnwm.cenapps.org.uk'
REACT_APP_API_KEY='222b17e018f94b5ea1c08913db98f298'
REACT_APP_API_HOST='https://wmca-api-portal-staging.azure-api.net'
2 changes: 1 addition & 1 deletion src/components/App/Form/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const Form = ({ prevMode }: { prevMode: string[] }) => {
const apiOptions = {
apiPath:
process.env.NODE_ENV === 'development'
? '/ticketing/tickets/search/complete'
? '/ticketing/v2/tickets/search/complete'
: '/ticketing/v2/tickets/search/complete',
};
const { getAPIResults, results, errorInfo, loading } = useTicketingAPI(apiOptions);
Expand Down
2 changes: 1 addition & 1 deletion src/components/App/Form/SidebarSummary/SidebarSummary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ const SidebarSummary = () => {
{ticketInfo.railZones && (
<>
<SummarySection
title="Rail zones"
title={ticketInfo.ticketType === 'tram' ? 'Metro zones' : 'Rail zones'}
value={<RailZoneSummary railZones={ticketInfo.railZones} />}
onChange={editMode !== 'railZones' ? () => editStep(2, 'railZones') : null}
disabled={!!formState.autoAnswered.railZones}
Expand Down
7 changes: 6 additions & 1 deletion src/components/App/Form/Step2/Step2.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useFormContext } from 'globalState';
import BusArea from './BusArea/BusArea';
import RailZone from './RailZone/RailZone';
import TramZone from './TramZone/TramZone';
import TravelTime from './TravelTime/TravelTime';

const Step2 = () => {
Expand All @@ -19,8 +20,12 @@ const Step2 = () => {
editMode === 'railZones'
) {
sectionToRender = <RailZone />;
} else if (
(formState.ticketInfo.ticketType === 'tram' && formState.ticketInfo.modes?.includes('tram')) ||
editMode === 'modes'
) {
sectionToRender = <TramZone />;
}

return <>{sectionToRender || <TravelTime />}</>;
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Refer to https://create-react-app.dev/docs/adding-a-css-modules-stylesheet
// On how to use scss modules with React

// Import WMN Design System scss for use in this scss file
// Tilde (~) represents the root folder(src)
@import '~assets/wmnds/vars';
@import '~assets/wmnds/mixins';

.trayHeader {
display: flex;
flex-direction: row-reverse;
justify-content: space-between;
align-items: flex-start;
flex-wrap: wrap;

h2 {
flex-grow: 1;
}
}

.addStation {
display: block;
}

.addBtn {
@media (max-width: $breakpoint-md - 1) {
display: block;
width: 100%;
text-align: left;
}
}

.traySearchContainer {
max-width: 27rem;
}

.btnLinkIconLeft {
display: inline-flex;
text-align: left;
align-items: center;

svg {
margin-right: $size-sm;
fill: currentColor;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { useContext } from 'react';
import { useFormContext } from 'globalState';
// Import components
import Button from 'components/shared/Button/Button';
// Import context
import { AutoCompleteContext } from './AutoCompleteContext';
import Result from './Result/Result';
import TrainAutoComplete from './TrainAutoComplete/TrainAutocomplete';
import s from './AutoComplete.module.scss';

const AutoComplete = () => {
const [{ selectedStations }, autoCompleteDispatch] = useContext(AutoCompleteContext);
const addStation = () => {
autoCompleteDispatch({ type: 'ADD_STATION' });
};
const [, formDispatch] = useFormContext();

const selectedStationIds = [...selectedStations.map((stn: any) => stn.id)];

let linkParams = '';
selectedStationIds.forEach((id, i) => {
if (id) {
linkParams += `&selectedStation${i}=${id}`;
}
});

const findRailZones = (url: string) => {
formDispatch({
type: 'REMOVE_TICKET_INFO',
payload: { name: 'railZones' },
});
window.location.href = url;
};
return (
<>
<div className={`${s.traySearchContainer}`}>
<div className="wmnds-m-b-md">
<TrainAutoComplete label="From:" id="autocomplete_from" queryId={0} />
</div>
<div className="wmnds-m-b-md">
<TrainAutoComplete label="To:" id="autocomplete_to" queryId={1} />
</div>
{selectedStations.length > 2 && (
<div className="wmnds-p-b-md">
<div className={`wmnds-inset-text wmnds-m-b-sm wmnds-p-r-none ${s.addStation}`}>
<div className="wmnds-fe-label">Add any additional tram stop(s) you travel to</div>
{selectedStations.slice(2).map((_station: any, i: number) => (
<TrainAutoComplete
id={`autocomplete_add${i + 2}`}
key={`autocomplete_add${i + 2}`}
queryId={i + 2}
/>
))}
</div>
</div>
)}
<Button
btnClass={`wmnds-btn--primary wmnds-m-b-lg ${s.addBtn}`}
iconRight="general-expand"
text="Add another stop"
onClick={addStation}
disabled={selectedStations.length >= 12}
/>
</div>
<Result />
<div className="wmnds-grid wmnds-grid--spacing-md-2-md">
<div className="wmnds-col-1 wmnds-col-md-1-2">
<Button
btnClass={`wmnds-btn--link ${s.btnLinkIconLeft}`}
text="View tram zones on a map"
iconLeft="general-location-pin"
onClick={() =>
findRailZones(`
https://tfwm-find-my-metro-zones.netlify.app/?ticketSearch=true${linkParams}
`)
}
/>
</div>
<div className="wmnds-col-1 wmnds-col-md-1-2">
<Button
btnClass={`wmnds-btn--link ${s.btnLinkIconLeft}`}
text="View tram zones in a list"
iconLeft="general-list"
onClick={() =>
findRailZones(
`https://tfwm-find-my-metro-zones.netlify.app?ticketSearch=true${linkParams}&mapView=false`,
)
}
/>
</div>
</div>
</>
);
};

export default AutoComplete;
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import React, { useReducer, createContext } from 'react';
// Import Helper functions
import {
getAllSearchParams,
setSearchParam,
getSearchParam,
delSearchParam,
} from 'globalState/helpers/URLSearchParams'; // (used to sync state with URL)

export const AutoCompleteContext = createContext(); // Create context

export const AutoCompleteProvider = (props) => {
const { children } = props || {};

// Get existing params which begin with 'query' and put them in an array
let additionalQueries = getAllSearchParams().filter((param) => param.name.match('^query'));
// Get existing params which begin with 'selectedStation' and put them in an array
let additionalStations = getAllSearchParams().filter((param) =>
param.name.match('^selectedStation'),
);

// Extract values and remove first 2 items as we already have them
additionalQueries = additionalQueries.slice(2).map((value) => value.id);
additionalStations = additionalStations.slice(2).map((value) => ({ id: value.id }));

// Set intial state
const initialState = {
queries: [getSearchParam('query0') || '', getSearchParam('query1') || '', ...additionalQueries],
// // The selected service is used to store details when a user has clicked an autocomplete
selectedStations: [
{
id: getSearchParam('selectedStation0') || null,
},
{
id: getSearchParam('selectedStation1') || null,
},
...additionalStations,
],
};

// Set up a reducer so we can change state based on centralised logic here
const reducer = (state, action) => {
// Update the query to what the user has typed
switch (action.type) {
case 'UPDATE_QUERY': {
setSearchParam(`query${action.queryId}`, action.payload);

const newState = state.queries;
newState[action.queryId] = action.payload;

return {
...state,
queries: [...newState],
};
}
// Update the state to show item user has selected
case 'UPDATE_SELECTED_STATION': {
const { id, queryId } = action.payload;
const item = `selectedStation${queryId}`;
setSearchParam(item, id); // Set URL

const newState = state.selectedStations;
newState[queryId] = action.payload;

return {
...state,
selectedStations: [...newState],
};
}
// Add new item for user to select
case 'ADD_STATION': {
const item = { id: null };

return {
...state,
selectedStations: [...state.selectedStations, item],
};
}

// Used to cancel selected service/station etc. This is mainly used when using from/to stations
case 'RESET_SELECTED_ITEM': {
const { queryId } = action.payload;
const item = `selectedStation${queryId}`;
const query = `query${queryId}`;

// Delete correct things from URL
delSearchParam(item);
delSearchParam(query);

// Update queries array in state
let newQueries = state.queries;
const newSelectedStations = state.selectedStations;

newQueries[queryId] = '';
if (queryId + 1 === state.selectedStations.length && queryId > 1) {
newSelectedStations.pop();
} else {
newSelectedStations[queryId] = { id: null };
}

// function to remove the last array value if it's empty (and not our initial 2 default values)
const removeLastValues = (array) => {
const newArray = array;
const lastItem = newArray[newArray.length - 1];
const valueToRemove =
typeof lastItem === 'object' ? lastItem.id === null : lastItem === '';
if (valueToRemove && newArray.length > 2) {
newArray.pop();
removeLastValues(newArray);
}
return newArray;
};

newQueries = removeLastValues(newQueries);
// Update state with deleted/cancelled service/item
return {
...state,
queries: [...newQueries],
selectedStations: [...newSelectedStations],
};
}

// Used to reset everything
case 'RESET_SELECTED_SERVICES':
getAllSearchParams().forEach((param) => {
delSearchParam(param.name);
});
return {
mapRef: state.mapRef,
queries: initialState.queries,
selectedStations: initialState.selectedStations,
};
// Default should return intial state if error
default:
return initialState;
}
};

// Set up reducer using reducer logic and initialState by default
const [autoCompleteState, autoCompleteDispatch] = useReducer(reducer, initialState);

// Pass state and dispatch in context and make accessible to children it wraps
return (
<AutoCompleteContext.Provider value={[autoCompleteState, autoCompleteDispatch]}>
{children}
</AutoCompleteContext.Provider>
);
};
Loading