diff options
Diffstat (limited to 'app/javascript/routes')
| -rw-r--r-- | app/javascript/routes/actions/index.js | 62 | ||||
| -rw-r--r-- | app/javascript/routes/components/App.js | 25 | ||||
| -rw-r--r-- | app/javascript/routes/components/BSelect2.js | 125 | ||||
| -rw-r--r-- | app/javascript/routes/components/OlMap.js | 169 | ||||
| -rw-r--r-- | app/javascript/routes/components/StopPoint.js | 94 | ||||
| -rw-r--r-- | app/javascript/routes/components/StopPointList.js | 69 | ||||
| -rw-r--r-- | app/javascript/routes/containers/AddStopPoint.js | 20 | ||||
| -rw-r--r-- | app/javascript/routes/containers/VisibleStopPoints.js | 58 | ||||
| -rw-r--r-- | app/javascript/routes/form_helper.js | 55 | ||||
| -rw-r--r-- | app/javascript/routes/index.js | 80 | ||||
| -rw-r--r-- | app/javascript/routes/reducers/index.js | 8 | ||||
| -rw-r--r-- | app/javascript/routes/reducers/stopPoints.js | 144 | ||||
| -rw-r--r-- | app/javascript/routes/show.js | 102 |
13 files changed, 1011 insertions, 0 deletions
diff --git a/app/javascript/routes/actions/index.js b/app/javascript/routes/actions/index.js new file mode 100644 index 000000000..13b2d60b2 --- /dev/null +++ b/app/javascript/routes/actions/index.js @@ -0,0 +1,62 @@ +const actions = { + addStop : () => { + return { + type: 'ADD_STOP' + } + }, + moveStopUp : (index) => { + return { + type: 'MOVE_STOP_UP', + index + } + }, + moveStopDown : (index) => { + return { + type: 'MOVE_STOP_DOWN', + index + } + }, + deleteStop : (index) => { + return { + type: 'DELETE_STOP', + index + } + }, + updateInputValue : (index, text) => { + return { + type : 'UPDATE_INPUT_VALUE', + index, + text + } + }, + updateSelectValue: (e, index) => { + return { + type :'UPDATE_SELECT_VALUE', + select_id: e.currentTarget.id, + select_value: e.currentTarget.value, + index + } + }, + toggleMap: (index) =>({ + type: 'TOGGLE_MAP', + index + }), + toggleEdit: (index) =>({ + type: 'TOGGLE_EDIT', + index + }), + closeMaps: () => ({ + type : 'CLOSE_MAP' + }), + selectMarker: (index, data) =>({ + type: 'SELECT_MARKER', + index, + data + }), + unselectMarker: (index) => ({ + type: 'UNSELECT_MARKER', + index + }) +} + +module.exports = actions diff --git a/app/javascript/routes/components/App.js b/app/javascript/routes/components/App.js new file mode 100644 index 000000000..0f5786407 --- /dev/null +++ b/app/javascript/routes/components/App.js @@ -0,0 +1,25 @@ +import React, { Component, PropTypes } from 'react' +import AddStopPoint from '../containers/AddStopPoint' +import VisibleStopPoints from'../containers/VisibleStopPoints' +import clone from '../../helpers/clone' +const I18n = clone(window , "I18n", true) + +export default class App extends Component { + + getChildContext() { + return { I18n } + } + + render() { + return ( + <div> + <VisibleStopPoints /> + <AddStopPoint /> + </div> + ) + } +} + +App.childContextTypes = { + I18n: PropTypes.object +} diff --git a/app/javascript/routes/components/BSelect2.js b/app/javascript/routes/components/BSelect2.js new file mode 100644 index 000000000..340d9df95 --- /dev/null +++ b/app/javascript/routes/components/BSelect2.js @@ -0,0 +1,125 @@ +import _ from'lodash' +import React, { Component, PropTypes } from 'react' +import Select2 from 'react-select2' + + +// get JSON full path +var origin = window.location.origin +var path = window.location.pathname.split('/', 3).join('/') + + +export default class BSelect3 extends Component { + constructor(props, context) { + super(props, context) + } + onChange(e) { + this.props.onChange(this.props.index, { + text: e.currentTarget.textContent, + stoparea_id: e.currentTarget.value, + user_objectid: e.params.data.user_objectid, + longitude: e.params.data.longitude, + latitude: e.params.data.latitude, + name: e.params.data.name, + short_name: e.params.data.short_name, + city_name: e.params.data.city_name, + area_type: e.params.data.area_type, + zip_code: e.params.data.zip_code, + comment: e.params.data.comment + }) + } + + parsedText(data) { + let a = data.replace('</em></small>', '') + let b = a.split('<small><em>') + if (b.length > 1) { + return ( + <span> + {b[0]} + <small><em>{b[1]}</em></small> + </span> + ) + } else { + return ( + <span>{data}</span> + ) + } + } + + render() { + if(this.props.value.edit) + return ( + <div className='select2-bootstrap-append'> + <BSelect2 {...this.props} onSelect={ this.onChange.bind(this) }/> + </div> + ) + else + if(!this.props.value.stoparea_id) + return ( + <div> + <BSelect2 {...this.props} onSelect={ this.onChange.bind(this) }/> + </div> + ) + else + return ( + <a + className='navlink' + href={origin + path + '/stop_areas/' + this.props.value.stoparea_id} + title="Voir l'arrêt" + > + {this.parsedText(this.props.value.text)} + </a> + ) + } +} + +class BSelect2 extends Component{ + componentDidMount() { + this.refs.newSelect.el.select2('open') + } + + render() { + return ( + <Select2 + value={ this.props.value.stoparea_id } + onSelect={ this.props.onSelect } + ref='newSelect' + options={{ + placeholder: this.context.I18n.routes.edit.select2.placeholder, + allowClear: true, + language: 'fr', /* Doesn't seem to work... :( */ + theme: 'bootstrap', + width: '100%', + ajax: { + url: origin + path + '/autocomplete_stop_areas.json', + dataType: 'json', + delay: '500', + data: function(params) { + return { + q: params.term, + target_type: 'zdep' + }; + }, + processResults: function(data, params) { + return { + results: data.map( + item => _.assign( + {}, + item, + { text: item.name + ", " + item.zip_code + " " + item.short_city_name + " <small><em>(" + item.user_objectid + ")</em></small>" } + ) + ) + }; + }, + cache: true + }, + escapeMarkup: function (markup) { return markup; }, + minimumInputLength: 3 + }} + /> + ) + } +} + +BSelect2.contextTypes = { + I18n: PropTypes.object +} diff --git a/app/javascript/routes/components/OlMap.js b/app/javascript/routes/components/OlMap.js new file mode 100644 index 000000000..2c01dfa7f --- /dev/null +++ b/app/javascript/routes/components/OlMap.js @@ -0,0 +1,169 @@ +import _ from 'lodash' +import React, { Component, PropTypes } from 'react' + +export default class OlMap extends Component{ + constructor(props, context){ + super(props, context) + } + + fetchApiURL(id){ + const origin = window.location.origin + const path = window.location.pathname.split('/', 3).join('/') + return origin + path + "/autocomplete_stop_areas/" + id + "/around?target_type=zdep" + } + + componentDidUpdate(prevProps, prevState){ + if(prevProps.value.olMap.isOpened == false && this.props.value.olMap.isOpened == true){ + var source = new ol.source.Vector({ + format: new ol.format.GeoJSON(), + url: this.fetchApiURL(this.props.value.stoparea_id) + }) + var feature = new ol.Feature({ + geometry: new ol.geom.Point(ol.proj.fromLonLat([parseFloat(this.props.value.longitude), parseFloat(this.props.value.latitude)])) + }) + + var defaultStyles = new ol.style.Style({ + image: new ol.style.Circle(({ + radius: 4, + fill: new ol.style.Fill({ + color: '#004d87' + }) + })) + }) + var selectedStyles = new ol.style.Style({ + image: new ol.style.Circle(({ + radius: 6, + fill: new ol.style.Fill({ + color: '#da2f36' + }) + })) + }) + + var centerLayer = new ol.layer.Vector({ + source: new ol.source.Vector({ + features: [feature] + }), + style: selectedStyles, + zIndex: 2 + }) + var vectorLayer = new ol.layer.Vector({ + source: source, + style: defaultStyles, + zIndex: 1 + }); + + var map = new ol.Map({ + target: 'stoppoint_map' + this.props.index, + layers: [ + new ol.layer.Tile({ + source: new ol.source.OSM() + }), + vectorLayer, + centerLayer + ], + controls: [ new ol.control.ScaleLine() ], + interactions: ol.interaction.defaults({ + dragPan: false, + doubleClickZoom: false, + shiftDragZoom: false, + mouseWheelZoom: false + }), + view: new ol.View({ + center: ol.proj.fromLonLat([parseFloat(this.props.value.longitude), parseFloat(this.props.value.latitude)]), + zoom: 18 + }) + }); + + // Selectable marker + var select = new ol.interaction.Select({ + style: selectedStyles + }); + + map.addInteraction(select); + + select.on('select', function(e) { + feature.setStyle(defaultStyles); + centerLayer.setZIndex(0); + + if(e.selected.length != 0) { + + if(e.selected[0].getGeometry() == feature.getGeometry()) { + if(e.selected[0].style_.image_.fill_.color_ != '#da2f36'){ + feature.setStyle(selectedStyles); + centerLayer.setZIndex(2); + e.preventDefault() + return false + } + } + let data = _.assign({}, e.selected[0].getProperties(), {geometry: undefined}); + + this.props.onSelectMarker(this.props.index, data) + } else { + this.props.onUnselectMarker(this.props.index) + } + }, this); + } + } + + render() { + if (this.props.value.olMap.isOpened) { + return ( + <div className='map_container'> + <div className='map_metas'> + <p> + <strong>{this.props.value.olMap.json.name}</strong> + </p> + <p> + <strong>{this.context.I18n.routes.edit.stop_point_type} : </strong> + {this.props.value.olMap.json.area_type} + </p> + <p> + <strong>{this.context.I18n.routes.edit.short_name} : </strong> + {this.props.value.olMap.json.short_name} + </p> + <p> + <strong>{this.context.I18n.id_reflex} : </strong> + {this.props.value.olMap.json.user_objectid} + </p> + + <p><strong>{this.context.I18n.routes.edit.map.coordinates} : </strong></p> + <p style={{paddingLeft: 10, marginTop: 0}}> + <em>{this.context.I18n.routes.edit.map.proj}.: </em>WSG84<br/> + <em>{this.context.I18n.routes.edit.map.lat}.: </em>{this.props.value.olMap.json.latitude} <br/> + <em>{this.context.I18n.routes.edit.map.lon}.: </em>{this.props.value.olMap.json.longitude} + </p> + <p> + <strong>{this.context.I18n.routes.edit.map.postal_code} : </strong> + {this.props.value.olMap.json.zip_code} + </p> + <p> + <strong>{this.context.I18n.routes.edit.map.city} : </strong> + {this.props.value.olMap.json.city_name} + </p> + <p> + <strong>{this.context.I18n.routes.edit.map.comment} : </strong> + {this.props.value.olMap.json.comment} + </p> + {(this.props.value.stoparea_id != this.props.value.olMap.json.stoparea_id) &&( + <div className='btn btn-outline-primary btn-sm' + onClick= {() => {this.props.onUpdateViaOlMap(this.props.index, this.props.value.olMap.json)}} + >{this.context.I18n.actions.select}</div> + )} + </div> + <div className='map_content'> + <div id={"stoppoint_map" + this.props.index} className='map'></div> + </div> + </div> + ) + } else { + return false + } + } +} + +OlMap.PropTypes = { +} + +OlMap.contextTypes = { + I18n: PropTypes.object +} diff --git a/app/javascript/routes/components/StopPoint.js b/app/javascript/routes/components/StopPoint.js new file mode 100644 index 000000000..606121f99 --- /dev/null +++ b/app/javascript/routes/components/StopPoint.js @@ -0,0 +1,94 @@ +import React, { PropTypes } from 'react' +import BSelect2 from './BSelect2' +import OlMap from './OlMap' + +export default function StopPoint(props, {I18n}) { + return ( + <div className='nested-fields'> + <div className='wrapper'> + <div style={{width: 90}}> + <span>{props.value.user_objectid}</span> + </div> + + <div> + <BSelect2 id={'route_stop_points_' + props.id} value={props.value} onChange={props.onChange} index={props.index} /> + </div> + + <div> + <select className='form-control' value={props.value.for_boarding} id="for_boarding" onChange={props.onSelectChange}> + <option value="normal">{I18n.routes.edit.stop_point.boarding.normal}</option> + <option value="forbidden">{I18n.routes.edit.stop_point.boarding.forbidden}</option> + </select> + </div> + + <div> + <select className='form-control' value={props.value.for_alighting} id="for_alighting" onChange={props.onSelectChange}> + <option value="normal">{I18n.routes.edit.stop_point.alighting.normal}</option> + <option value="forbidden">{I18n.routes.edit.stop_point.alighting.forbidden}</option> + </select> + </div> + + <div className='actions-5'> + <div + className={'btn btn-link' + (props.value.stoparea_id ? '' : ' disabled')} + onClick={props.onToggleMap} + > + <span className='fa fa-map-marker'></span> + </div> + + <div + className={'btn btn-link' + (props.first ? ' disabled' : '')} + onClick={props.onMoveUpClick} + > + <span className='fa fa-arrow-up'></span> + </div> + <div + className={'btn btn-link' + (props.last ? ' disabled' : '')} + onClick={props.onMoveDownClick} + > + <span className='fa fa-arrow-down'></span> + </div> + + <div + className='btn btn-link' + onClick={props.onToggleEdit} + > + <span className={'fa' + (props.value.edit ? ' fa-check' : ' fa-pencil')}></span> + </div> + <div + className='btn btn-link' + onClick={props.onDeleteClick} + > + <span className='fa fa-trash text-danger'></span> + </div> + </div> + </div> + + <OlMap + value = {props.value} + index = {props.index} + onSelectMarker = {props.onSelectMarker} + onUnselectMarker = {props.onUnselectMarker} + onUpdateViaOlMap = {props.onUpdateViaOlMap} + /> + </div> + ) +} + +StopPoint.PropTypes = { + onToggleMap: PropTypes.func.isRequired, + onToggleEdit: PropTypes.func.isRequired, + onDeleteClick: PropTypes.func.isRequired, + onMoveUpClick: PropTypes.func.isRequired, + onMoveDownClick: PropTypes.func.isRequired, + onChange: PropTypes.func.isRequired, + onSelectChange: PropTypes.func.isRequired, + first: PropTypes.bool, + last: PropTypes.bool, + index: PropTypes.number, + value: PropTypes.object +} + +StopPoint.contextTypes = { + I18n: PropTypes.object +}
\ No newline at end of file diff --git a/app/javascript/routes/components/StopPointList.js b/app/javascript/routes/components/StopPointList.js new file mode 100644 index 000000000..68af16f57 --- /dev/null +++ b/app/javascript/routes/components/StopPointList.js @@ -0,0 +1,69 @@ +import React, { PropTypes } from 'react' +import StopPoint from './StopPoint' + +export default function StopPointList({ stopPoints, onDeleteClick, onMoveUpClick, onMoveDownClick, onChange, onSelectChange, onToggleMap, onToggleEdit, onSelectMarker, onUnselectMarker, onUpdateViaOlMap }, {I18n}) { + return ( + <div className='subform'> + <div className='nested-head'> + <div className="wrapper"> + <div style={{width: 100}}> + <div className="form-group"> + <label className="control-label">{I18n.reflex_id}</label> + </div> + </div> + <div> + <div className="form-group"> + <label className="control-label">{I18n.simple_form.labels.stop_point.name}</label> + </div> + </div> + <div> + <div className="form-group"> + <label className="control-label">{I18n.simple_form.labels.stop_point.for_boarding}</label> + </div> + </div> + <div> + <div className="form-group"> + <label className="control-label">{I18n.simple_form.labels.stop_point.for_alighting}</label> + </div> + </div> + <div className='actions-5'></div> + </div> + </div> + {stopPoints.map((stopPoint, index) => + <StopPoint + key={'item-' + index} + onDeleteClick={() => onDeleteClick(index)} + onMoveUpClick={() => { + onMoveUpClick(index) + }} + onMoveDownClick={() => onMoveDownClick(index)} + onChange={ onChange } + onSelectChange={ (e) => onSelectChange(e, index) } + onToggleMap={() => onToggleMap(index)} + onToggleEdit={() => onToggleEdit(index)} + onSelectMarker={onSelectMarker} + onUnselectMarker={onUnselectMarker} + onUpdateViaOlMap={onUpdateViaOlMap} + first={ index === 0 } + last={ index === (stopPoints.length - 1) } + index={ index } + value={ stopPoint } + /> + )} + </div> + ) +} + +StopPointList.PropTypes = { + stopPoints: PropTypes.array.isRequired, + onDeleteClick: PropTypes.func.isRequired, + onMoveUpClick: PropTypes.func.isRequired, + onMoveDownClick: PropTypes.func.isRequired, + onSelectChange: PropTypes.func.isRequired, + onSelectMarker: PropTypes.func.isRequired, + onUnselectMarker : PropTypes.func.isRequired +} + +StopPointList.contextTypes = { + I18n: PropTypes.object +}
\ No newline at end of file diff --git a/app/javascript/routes/containers/AddStopPoint.js b/app/javascript/routes/containers/AddStopPoint.js new file mode 100644 index 000000000..fd9227ff3 --- /dev/null +++ b/app/javascript/routes/containers/AddStopPoint.js @@ -0,0 +1,20 @@ +import React from 'react' +import { connect } from 'react-redux' +import actions from '../actions' + +let AddStopPoint = ({ dispatch }) => { + return ( + <div className="nested-linker"> + <form onSubmit={e => { + e.preventDefault() + dispatch(actions.closeMaps()) + dispatch(actions.addStop()) + }}> + <button type="submit" className="btn btn-outline-primary"> + Ajouter un arrêt + </button> + </form> + </div> + ) +} +export default AddStopPoint = connect()(AddStopPoint) diff --git a/app/javascript/routes/containers/VisibleStopPoints.js b/app/javascript/routes/containers/VisibleStopPoints.js new file mode 100644 index 000000000..67d77af50 --- /dev/null +++ b/app/javascript/routes/containers/VisibleStopPoints.js @@ -0,0 +1,58 @@ +import actions from '../actions' +import { connect } from 'react-redux' +import StopPointList from '../components/StopPointList' + +const mapStateToProps = (state) => { + return { + stopPoints: state.stopPoints + } +} + +const mapDispatchToProps = (dispatch) => { + return { + onDeleteClick: (index) =>{ + dispatch(actions.deleteStop(index)) + dispatch(actions.closeMaps()) + }, + onMoveUpClick: (index) =>{ + dispatch(actions.moveStopUp(index)) + dispatch(actions.closeMaps()) + }, + onMoveDownClick: (index) =>{ + dispatch(actions.moveStopDown(index)) + dispatch(actions.closeMaps()) + }, + onChange: (index, text) =>{ + dispatch(actions.updateInputValue(index, text)) + dispatch(actions.closeMaps()) + dispatch(actions.toggleEdit(index)) + }, + onSelectChange: (e, index) =>{ + dispatch(actions.updateSelectValue(e, index)) + dispatch(actions.closeMaps()) + }, + onToggleMap: (index) =>{ + dispatch(actions.toggleMap(index)) + }, + onToggleEdit: (index) =>{ + dispatch(actions.toggleEdit(index)) + }, + onSelectMarker: (index, data) =>{ + dispatch(actions.selectMarker(index, data)) + }, + onUnselectMarker: (index) =>{ + dispatch(actions.unselectMarker(index)) + }, + onUpdateViaOlMap: (index, data) =>{ + dispatch(actions.updateInputValue(index, data)) + dispatch(actions.toggleMap(index)) + } + } +} + +const VisibleStopPoints = connect( + mapStateToProps, + mapDispatchToProps +)(StopPointList) + +export default VisibleStopPoints diff --git a/app/javascript/routes/form_helper.js b/app/javascript/routes/form_helper.js new file mode 100644 index 000000000..8a3277234 --- /dev/null +++ b/app/javascript/routes/form_helper.js @@ -0,0 +1,55 @@ +const formHelper = { + addInput: (name, value, index) => { + let form = document.querySelector('form') + let input = document.createElement('input') + let formatedName = `route[stop_points_attributes][${index.toString()}][${name}]` + input.setAttribute('type', 'hidden') + input.setAttribute('name', formatedName) + input.setAttribute('value', value) + form.appendChild(input) + }, + addError: (ids) => { + ids.forEach((id) => { + if (!$(id).parents('.form-group').hasClass('has-error')) { + $(id).parents('.form-group').addClass('has-error') + $(id).parent().append(`<span class='help-block small'>${'doit être rempli(e)'}</span>`) + } + }) + }, + cleanInputs: (ids) => { + ids.forEach((id) =>{ + $(id).parents('.form-group').removeClass('has-error') + $(id).siblings('span').remove() + }) + }, + handleForm: (...ids) => { + let filledInputs = [] + let blankInputs = [] + ids.forEach(id => { + $(id).val() == "" ? blankInputs.push(id) : filledInputs.push(id) + }) + + if (filledInputs.length > 0) formHelper.cleanInputs(filledInputs) + if (blankInputs.length > 0) formHelper.addError(blankInputs) + }, + handleStopPoints: (event, state) => { + if (state.stopPoints.length >= 2) { + state.stopPoints.map((stopPoint, i) => { + formHelper.addInput('id', stopPoint.stoppoint_id ? stopPoint.stoppoint_id : '', i) + formHelper.addInput('stop_area_id', stopPoint.stoparea_id, i) + formHelper.addInput('position', i, i) + formHelper.addInput('for_boarding', stopPoint.for_boarding, i) + formHelper.addInput('for_alighting', stopPoint.for_alighting, i) + }) + if ($('.alert.alert-danger').length > 0) $('.alert.alert-danger').remove() + } else { + event.preventDefault() + let msg = "L'itinéraire doit comporter au moins deux arrêts" + if ($('.alert.alert-danger').length == 0) { + $('#stop_points').find('.subform').after(`<div class='alert alert-danger'><span class='fa fa-lg fa-exclamation-circle'></span><span>" ${msg} "</span></div>`) + } + } + } +} + +export default formHelper
\ No newline at end of file diff --git a/app/javascript/routes/index.js b/app/javascript/routes/index.js new file mode 100644 index 000000000..febae7d54 --- /dev/null +++ b/app/javascript/routes/index.js @@ -0,0 +1,80 @@ +import React from 'react' +import { render } from 'react-dom' +import { Provider } from 'react-redux' +import { createStore } from 'redux' +import reducers from './reducers' +import App from './components/App' +import { handleForm, handleStopPoints } from './form_helper' +import clone from '../helpers/clone' +let datas = clone(window, "itinerary_stop", true) +datas = JSON.parse(decodeURIComponent(datas)) + +// logger, DO NOT REMOVE +// var applyMiddleware = require('redux').applyMiddleware +// var createLogger = require('redux-logger') +// var thunkMiddleware = require('redux-thunk').default +// var promise = require('redux-promise') + +const getInitialState = () => { + let state = [] + + datas.map(function(v, i) { + let fancyText = v.name.replace("'", "\'") + if(v.zip_code && v.city_name) + fancyText += ", " + v.zip_code + " " + v.city_name.replace("'", "\'") + + state.push({ + stoppoint_id: v.stoppoint_id, + stoparea_id: v.stoparea_id, + user_objectid: v.user_objectid, + short_name: v.short_name ? v.short_name.replace("'", "\'") : '', + area_type: v.area_type, + index: i, + edit: false, + city_name: v.city_name ? v.city_name.replace("'", "\'") : '', + zip_code: v.zip_code, + name: v.name ? v.name.replace("'", "\'") : '', + registration_number: v.registration_number, + text: fancyText, + for_boarding: v.for_boarding || "normal", + for_alighting: v.for_alighting || "normal", + longitude: v.longitude || 0, + latitude: v.latitude || 0, + comment: v.comment ? v.comment.replace("'", "\'") : '', + olMap: { + isOpened: false, + json: {} + } + }) + }) + + return state +} + +var initialState = {stopPoints: getInitialState()} +// const loggerMiddleware = createLogger() +let store = createStore( + reducers, + initialState + // applyMiddleware(thunkMiddleware, promise, loggerMiddleware) +) + +render( + <Provider store={store}> + <App /> + </Provider>, + document.getElementById('stop_points') +) + +document.querySelector('input[name=commit]').addEventListener('click', (event)=>{ + let state = store.getState() + + let name = $("#route_name").val() + let publicName = $("#route_published_name").val() + if (name == "" || publicName == "") { + event.preventDefault() + handleForm("#route_name", "#route_published_name") + } + + handleStopPoints(event, state) +}) diff --git a/app/javascript/routes/reducers/index.js b/app/javascript/routes/reducers/index.js new file mode 100644 index 000000000..eb01ea9f7 --- /dev/null +++ b/app/javascript/routes/reducers/index.js @@ -0,0 +1,8 @@ +import { combineReducers } from 'redux' +import stopPoints from './stopPoints' + +const stopPointsApp = combineReducers({ + stopPoints +}) + +export default stopPointsApp diff --git a/app/javascript/routes/reducers/stopPoints.js b/app/javascript/routes/reducers/stopPoints.js new file mode 100644 index 000000000..eeec06327 --- /dev/null +++ b/app/javascript/routes/reducers/stopPoints.js @@ -0,0 +1,144 @@ +import _ from 'lodash' +import formHelper from '../form_helper' + +const stopPoint = (state = {}, action, length) => { + switch (action.type) { + case 'ADD_STOP': + return { + text: '', + index: length, + edit: true, + for_boarding: 'normal', + for_alighting: 'normal', + olMap: { + isOpened: false, + json: {} + } + } + default: + return state + } +} + +const updateFormForDeletion = (stop) =>{ + if (stop.stoppoint_id !== undefined){ + let now = Date.now() + formHelper.addInput('id', stop.stoppoint_id, now) + formHelper.addInput('_destroy', 'true', now) + } +} + +const stopPoints = (state = [], action) => { + switch (action.type) { + case 'ADD_STOP': + return [ + ...state, + stopPoint(undefined, action, state.length) + ] + case 'MOVE_STOP_UP': + return [ + ...state.slice(0, action.index - 1), + _.assign({}, state[action.index], { stoppoint_id: state[action.index - 1].stoppoint_id }), + _.assign({}, state[action.index - 1], { stoppoint_id: state[action.index].stoppoint_id }), + ...state.slice(action.index + 1) + ] + case 'MOVE_STOP_DOWN': + return [ + ...state.slice(0, action.index), + _.assign({}, state[action.index + 1], { stoppoint_id: state[action.index].stoppoint_id }), + _.assign({}, state[action.index], { stoppoint_id: state[action.index + 1].stoppoint_id }), + ...state.slice(action.index + 2) + ] + case 'DELETE_STOP': + updateFormForDeletion(state[action.index]) + return [ + ...state.slice(0, action.index), + ...state.slice(action.index + 1).map((stopPoint)=>{ + stopPoint.index-- + return stopPoint + }) + ] + case 'UPDATE_INPUT_VALUE': + return state.map( (t, i) => { + if (i === action.index) { + return _.assign( + {}, + t, + { + stoppoint_id: t.stoppoint_id, + text: action.text.text, + stoparea_id: action.text.stoparea_id, + user_objectid: action.text.user_objectid, + latitude: action.text.latitude, + longitude: action.text.longitude, + name: action.text.name, + short_name: action.text.short_name, + area_type: action.text.area_type, + city_name: action.text.city_name, + comment: action.text.comment, + registration_number: action.text.registration_number + } + ) + } else { + return t + } + }) + case 'UPDATE_SELECT_VALUE': + return state.map( (t, i) => { + if (i === action.index) { + let stopState = _.assign({}, t) + stopState[action.select_id] = action.select_value + return stopState + } else { + return t + } + }) + case 'TOGGLE_EDIT': + return state.map((t, i) => { + if (i === action.index){ + return _.assign({}, t, {edit: !t.edit}) + } else { + return t + } + }) + case 'TOGGLE_MAP': + return state.map( (t, i) => { + if (i === action.index){ + let val = !t.olMap.isOpened + let jsonData = val ? _.assign({}, t, {olMap: undefined}) : {} + let stateMap = _.assign({}, t.olMap, {isOpened: val, json: jsonData}) + return _.assign({}, t, {olMap: stateMap}) + }else { + let emptyMap = _.assign({}, t.olMap, {isOpened: false, json : {}}) + return _.assign({}, t, {olMap: emptyMap}) + } + }) + case 'SELECT_MARKER': + return state.map((t, i) => { + if (i === action.index){ + let stateMap = _.assign({}, t.olMap, {json: action.data}) + return _.assign({}, t, {olMap: stateMap}) + } else { + return t + } + }) + case 'UNSELECT_MARKER': + return state.map((t, i) => { + if (i === action.index){ + let stateMap = _.assign({}, t.olMap, {json: {}}) + return _.assign({}, t, {olMap: stateMap}) + } else { + return t + } + }) + case 'CLOSE_MAP': + return state.map( (t, i) => { + let emptyMap = _.assign({}, t.olMap, {isOpened: false, json: {}}) + return _.assign({}, t, {olMap: emptyMap}) + }) + default: + return state + } +} + +export default stopPoints
\ No newline at end of file diff --git a/app/javascript/routes/show.js b/app/javascript/routes/show.js new file mode 100644 index 000000000..e88469900 --- /dev/null +++ b/app/javascript/routes/show.js @@ -0,0 +1,102 @@ +const clone = require('../helpers/clone') +let route = clone(window, "route", true) +route = JSON.parse(decodeURIComponent(route)) + +const geoColPts = [] +const geoColLns= [] +const geoColEdges = [ + new ol.Feature({ + geometry: new ol.geom.Point(ol.proj.fromLonLat([parseFloat(route[0].longitude), parseFloat(route[0].latitude)])) + }), + new ol.Feature({ + geometry: new ol.geom.Point(ol.proj.fromLonLat([parseFloat(route[route.length - 1].longitude), parseFloat(route[route.length - 1].latitude)])) + }) +] +route.forEach(function(stop, i){ + if (i < route.length - 1){ + geoColLns.push(new ol.Feature({ + geometry: new ol.geom.LineString([ + ol.proj.fromLonLat([parseFloat(route[i].longitude), parseFloat(route[i].latitude)]), + ol.proj.fromLonLat([parseFloat(route[i+1].longitude), parseFloat(route[i+1].latitude)]) + ]) + })) + } + geoColPts.push(new ol.Feature({ + geometry: new ol.geom.Point(ol.proj.fromLonLat([parseFloat(stop.longitude), parseFloat(stop.latitude)])) + }) + ) +}) +var edgeStyles = new ol.style.Style({ + image: new ol.style.Circle(({ + radius: 5, + stroke: new ol.style.Stroke({ + color: '#007fbb', + width: 2 + }), + fill: new ol.style.Fill({ + color: '#007fbb', + width: 2 + }) + })) +}) +var defaultStyles = new ol.style.Style({ + image: new ol.style.Circle(({ + radius: 4, + stroke: new ol.style.Stroke({ + color: '#007fbb', + width: 2 + }), + fill: new ol.style.Fill({ + color: '#ffffff', + width: 2 + }) + })) +}) +var lineStyle = new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: '#007fbb', + width: 3 + }) +}) + +var vectorPtsLayer = new ol.layer.Vector({ + source: new ol.source.Vector({ + features: geoColPts + }), + style: defaultStyles, + zIndex: 2 +}) +var vectorEdgesLayer = new ol.layer.Vector({ + source: new ol.source.Vector({ + features: geoColEdges + }), + style: edgeStyles, + zIndex: 3 +}) +var vectorLnsLayer = new ol.layer.Vector({ + source: new ol.source.Vector({ + features: geoColLns + }), + style: [lineStyle], + zIndex: 1 +}) + +var map = new ol.Map({ + target: 'route_map', + layers: [ + new ol.layer.Tile({ + source: new ol.source.OSM() + }), + vectorPtsLayer, + vectorEdgesLayer, + vectorLnsLayer + ], + controls: [ new ol.control.ScaleLine(), new ol.control.Zoom(), new ol.control.ZoomSlider() ], + interactions: ol.interaction.defaults({ + zoom: true + }), + view: new ol.View({ + center: ol.proj.fromLonLat([parseFloat(route[0].longitude), parseFloat(route[0].latitude)]), + zoom: 13 + }) +}); |
