From 5f7e05a326f734ba51c59c1877cfe9c13a4299b9 Mon Sep 17 00:00:00 2001 From: e-halinen Date: Tue, 10 Sep 2024 15:08:50 +0300 Subject: [PATCH] AB#32085: Improve search UX, add capability to generate multiple line timetables at once --- src/components/Generator.js | 36 ++++++++++++++++++++++++-- src/components/LineSelect.js | 50 +++++++++++++++++++++++++++++++----- src/stores/commonStore.js | 22 +++++++++++++++- src/stores/generatorStore.js | 12 ++++++--- src/util/api.js | 39 ++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 14 deletions(-) diff --git a/src/components/Generator.js b/src/components/Generator.js index cc6dc81..f85d43e 100644 --- a/src/components/Generator.js +++ b/src/components/Generator.js @@ -60,6 +60,31 @@ const Generator = props => { .map(({ stopIds }) => (generatorStore.component === 'TerminalPoster' ? 1 : stopIds.length)) .reduce((prev, cur) => prev + cur, 0); + const getAmount = () => { + let text = ''; + switch (generatorStore.component) { + case 'StopPoster': + text = stopCount; + break; + + case 'Timetable': + text = stopCount; + break; + + case 'TerminalPoster': + text = 1; + break; + + case 'LineTimetable': + text = generatorStore.selectedLines.length; + break; + default: + text = 0; + break; + } + return text; + }; + return ( @@ -165,7 +190,14 @@ const Generator = props => { {generatorStore.component === 'LineTimetable' && (
- +
)} @@ -300,7 +332,7 @@ const Generator = props => { generatorStore.generate(); } }} - label={`Generoi (${generatorStore.component !== 'TerminalPoster' ? stopCount : 1})`} + label={`Generoi (${getAmount()})`} style={{ height: 40, marginLeft: 10 }} primary /> diff --git a/src/components/LineSelect.js b/src/components/LineSelect.js index d10106f..3471fa6 100644 --- a/src/components/LineSelect.js +++ b/src/components/LineSelect.js @@ -1,30 +1,66 @@ import React from 'react'; import PropTypes from 'prop-types'; import { observer } from 'mobx-react'; - import styled from 'styled-components'; -import { TextField } from 'material-ui'; +import { ListItem, TextField } from 'material-ui'; -const SectionHeading = styled.h4` +const SectionHeading = styled.h3` margin-bottom: 0.5rem; `; +const ListItemTitle = styled.h4` + display: inline; + width: fit-content; +`; + +const SelectedLinesTitle = styled.h3` + font-size: 1.2rem; +`; + +const ListContainer = styled.div` + flex-grow: 1; + max-height: 500px; + overflow-y: scroll; +`; + +const mapLineItems = (lines, onClick) => { + if (lines.length > 0) { + return lines.map((line, index) => ( + onClick(line)} key={index}> + {line.lineIdParsed}

{line.nameFi}

+
+ )); + } + return

Haulla ei löytynyt linjoja

; +}; + const LineSelect = props => (
- Linja + Linja-aikataulu - event.target.value ? props.onChange(event.target.value) : props.onChange('') + event.target.value ? props.setLineQuery(event.target.value) : props.setLineQuery('') } + value={props.lineQuery} style={{ width: '100%' }} /> + {mapLineItems(props.lines, props.addLine)} +
+ Generoitavat linja-aikataulut + {mapLineItems(props.selectedLines, props.removeLine)} +
); LineSelect.propTypes = { - onChange: PropTypes.func.isRequired, + setLineQuery: PropTypes.func.isRequired, + lineQuery: PropTypes.string.isRequired, + lines: PropTypes.object.isRequired, + addLine: PropTypes.func.isRequired, + removeLine: PropTypes.func.isRequired, + selectedLines: PropTypes.object.isRequired, }; export default observer(LineSelect); diff --git a/src/stores/commonStore.js b/src/stores/commonStore.js index 84bdd33..34cd5cc 100644 --- a/src/stores/commonStore.js +++ b/src/stores/commonStore.js @@ -16,9 +16,10 @@ import { removeTemplate, getImages, removeImage, + getAllLines, } from '../util/api'; import get from 'lodash/get'; -import { isEmpty } from 'lodash'; +import { filter, isEmpty } from 'lodash'; import reduce from 'lodash/reduce'; import generatorStore from './generatorStore'; @@ -36,6 +37,8 @@ const store = observable({ images: [], selectedTemplate: null, prevSavedTemplate: null, + lines: [], + lineQuery: '', get currentTemplate() { const { selectedTemplate, templates } = store; const currentTemplate = templates.find(template => template.id === selectedTemplate); @@ -355,4 +358,21 @@ store.setRouteFilter = value => { store.routeFilter = value; }; +store.setLineQuery = async query => { + store.lineQuery = query; + const results = await store.getLines(); + store.lines = results; +}; + +store.getLines = async () => { + const { data } = await getAllLines(); + const filteredLines = filter( + data.allLines.nodes, + line => + line.lineId.toLowerCase().includes(store.lineQuery.toLowerCase()) || + line.nameFi.toLowerCase().includes(store.lineQuery.toLowerCase()), + ); + return filteredLines; +}; + export default store; diff --git a/src/stores/generatorStore.js b/src/stores/generatorStore.js index 40350ea..8bc3c31 100644 --- a/src/stores/generatorStore.js +++ b/src/stores/generatorStore.js @@ -42,7 +42,7 @@ const store = observable({ minimapZoneSymbols: true, legend: true, isSmallTerminalPoster: false, - lineId: '', + selectedLines: [], get rows() { let rows = []; @@ -154,8 +154,12 @@ store.setIsSmallTerminalPoster = () => { store.isSmallTerminalPoster = !store.isSmallTerminalPoster; }; -store.setLineId = value => { - store.lineId = value; +store.addLine = line => { + store.selectedLines.push(line); +}; + +store.removeLine = line => { + store.selectedLines.remove(line); }; store.generate = () => { @@ -213,7 +217,7 @@ store.generate = () => { break; case 'LineTimetable': - props = [lineTimetablePropsTemplate(store.lineId)]; + props = store.selectedLines.map(line => lineTimetablePropsTemplate(line.lineId)); break; default: diff --git a/src/util/api.js b/src/util/api.js index c4f11ce..cb82d87 100644 --- a/src/util/api.js +++ b/src/util/api.js @@ -109,6 +109,44 @@ async function getTerminals() { return get(terminalData, 'data.allTerminals.nodes', []); } +async function getAllLines() { + const link = new HttpLink({ uri: JORE_API_URL }); + + const query = { + query: gql` + query AllLinesQuery { + allLines { + nodes { + lineId + nameFi + dateBegin + dateEnd + trunkRoute + lineIdParsed + routes { + totalCount + nodes { + mode + type + } + } + } + } + } + `, + }; + + let results; + + try { + results = await makePromise(execute(link, query)); + } catch (err) { + throw new Error(err.message); + } + + return results; +} + function getBuilds() { return getJson('builds'); } @@ -182,6 +220,7 @@ export { getTerminals, getBuilds, getImages, + getAllLines, removeImage, getTemplates, addTemplate,