aboutsummaryrefslogtreecommitdiffstats
path: root/app/javascript/journey_patterns
diff options
context:
space:
mode:
authorcedricnjanga2017-10-06 10:17:17 +0200
committercedricnjanga2017-10-06 10:17:17 +0200
commitb6f08e58fae35d5dd8a610af31c2950b37746695 (patch)
tree989843dd674c41ff73eb75bd630ce4cc91fff91b /app/javascript/journey_patterns
parent08517c27551a2dd8b227f6662f4c41574a36d81e (diff)
downloadchouette-core-b6f08e58fae35d5dd8a610af31c2950b37746695.tar.bz2
Add webpacker gem and migrate the React apps
Diffstat (limited to 'app/javascript/journey_patterns')
-rw-r--r--app/javascript/journey_patterns/actions/index.js220
-rw-r--r--app/javascript/journey_patterns/components/App.js21
-rw-r--r--app/javascript/journey_patterns/components/ConfirmModal.js46
-rw-r--r--app/javascript/journey_patterns/components/CreateModal.js122
-rw-r--r--app/javascript/journey_patterns/components/EditModal.js110
-rw-r--r--app/javascript/journey_patterns/components/JourneyPattern.js131
-rw-r--r--app/javascript/journey_patterns/components/JourneyPatterns.js155
-rw-r--r--app/javascript/journey_patterns/components/Navigate.js62
-rw-r--r--app/javascript/journey_patterns/components/SaveJourneyPattern.js39
-rw-r--r--app/javascript/journey_patterns/containers/AddJourneyPattern.js30
-rw-r--r--app/javascript/journey_patterns/containers/ConfirmModal.js30
-rw-r--r--app/javascript/journey_patterns/containers/JourneyPatternList.js34
-rw-r--r--app/javascript/journey_patterns/containers/Modal.js26
-rw-r--r--app/javascript/journey_patterns/containers/Navigate.js15
-rw-r--r--app/javascript/journey_patterns/containers/SaveJourneyPattern.js27
-rw-r--r--app/javascript/journey_patterns/reducers/editMode.js10
-rw-r--r--app/javascript/journey_patterns/reducers/index.js18
-rw-r--r--app/javascript/journey_patterns/reducers/journeyPatterns.js90
-rw-r--r--app/javascript/journey_patterns/reducers/modal.js41
-rw-r--r--app/javascript/journey_patterns/reducers/pagination.js35
-rw-r--r--app/javascript/journey_patterns/reducers/status.js21
-rw-r--r--app/javascript/journey_patterns/reducers/stopPointsList.js6
22 files changed, 1289 insertions, 0 deletions
diff --git a/app/javascript/journey_patterns/actions/index.js b/app/javascript/journey_patterns/actions/index.js
new file mode 100644
index 000000000..0c1cb5f5c
--- /dev/null
+++ b/app/javascript/journey_patterns/actions/index.js
@@ -0,0 +1,220 @@
+import Promise from 'promise-polyfill'
+
+// To add to window
+if (!window.Promise) {
+ window.Promise = Promise;
+}
+
+const actions = {
+ enterEditMode: () => ({
+ type: "ENTER_EDIT_MODE"
+ }),
+ exitEditMode: () => ({
+ type: "EXIT_EDIT_MODE"
+ }),
+ receiveJourneyPatterns : (json) => ({
+ type: "RECEIVE_JOURNEY_PATTERNS",
+ json
+ }),
+ receiveErrors : (json) => ({
+ type: "RECEIVE_ERRORS",
+ json
+ }),
+ unavailableServer : () => ({
+ type: 'UNAVAILABLE_SERVER'
+ }),
+ goToPreviousPage : (dispatch, pagination) => ({
+ type: 'GO_TO_PREVIOUS_PAGE',
+ dispatch,
+ pagination,
+ nextPage : false
+ }),
+ goToNextPage : (dispatch, pagination) => ({
+ type: 'GO_TO_NEXT_PAGE',
+ dispatch,
+ pagination,
+ nextPage : true
+ }),
+ updateCheckboxValue : (e, index) => ({
+ type : 'UPDATE_CHECKBOX_VALUE',
+ id : e.currentTarget.id,
+ index
+ }),
+ checkConfirmModal : (event, callback, stateChanged,dispatch) => {
+ if(stateChanged === true){
+ return actions.openConfirmModal(callback)
+ }else{
+ dispatch(actions.fetchingApi())
+ return callback
+ }
+ },
+ openConfirmModal : (callback) => ({
+ type : 'OPEN_CONFIRM_MODAL',
+ callback
+ }),
+ openEditModal : (index, journeyPattern) => ({
+ type : 'EDIT_JOURNEYPATTERN_MODAL',
+ index,
+ journeyPattern
+ }),
+ openCreateModal : () => ({
+ type : 'CREATE_JOURNEYPATTERN_MODAL'
+ }),
+ deleteJourneyPattern : (index) => ({
+ type : 'DELETE_JOURNEYPATTERN',
+ index,
+ }),
+ closeModal : () => ({
+ type : 'CLOSE_MODAL'
+ }),
+ saveModal : (index, data) => ({
+ type: 'SAVE_MODAL',
+ data,
+ index
+ }),
+ addJourneyPattern : (data) => ({
+ type: 'ADD_JOURNEYPATTERN',
+ data,
+ }),
+ savePage : (dispatch, currentPage) => ({
+ type: 'SAVE_PAGE',
+ dispatch
+ }),
+ updateTotalCount: (diff) => ({
+ type: 'UPDATE_TOTAL_COUNT',
+ diff
+ }),
+ fetchingApi: () =>({
+ type: 'FETCH_API'
+ }),
+ resetValidation: (target) => {
+ $(target).parent().removeClass('has-error').children('.help-block').remove()
+ },
+ humanOID : (oid) => oid.split(':')[2].split("-").pop(),
+ validateFields : (fields) => {
+ const test = []
+
+ Object.keys(fields).map(function(key) {
+ test.push(fields[key].validity.valid)
+ })
+ if(test.indexOf(false) >= 0) {
+ // Form is invalid
+ test.map(function(item, i) {
+ if(item == false) {
+ const k = Object.keys(fields)[i]
+ $(fields[k]).parent().addClass('has-error').children('.help-block').remove()
+ $(fields[k]).parent().append("<span class='small help-block'>" + fields[k].validationMessage + "</span>")
+ }
+ })
+ return false
+ } else {
+ // Form is valid
+ return true
+ }
+ },
+ submitJourneyPattern : (dispatch, state, next) => {
+ dispatch(actions.fetchingApi())
+ let urlJSON = window.location.pathname + ".json"
+ let hasError = false
+ fetch(urlJSON, {
+ credentials: 'same-origin',
+ method: 'PATCH',
+ contentType: 'application/json; charset=utf-8',
+ Accept: 'application/json',
+ body: JSON.stringify(state),
+ headers: {
+ 'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
+ }
+ }).then(response => {
+ if(!response.ok) {
+ hasError = true
+ }
+ return response.json()
+ }).then((json) => {
+ if(hasError == true) {
+ dispatch(actions.receiveErrors(json))
+ } else {
+ if(next) {
+ dispatch(next)
+ } else {
+ if(json.length != window.currentItemsLength){
+ dispatch(actions.updateTotalCount(window.currentItemsLength - json.length))
+ }
+ window.currentItemsLength = json.length
+ dispatch(actions.exitEditMode())
+ dispatch(actions.receiveJourneyPatterns(json))
+ }
+ }
+ })
+ },
+ fetchJourneyPatterns : (dispatch, currentPage, nextPage) => {
+ if(currentPage == undefined){
+ currentPage = 1
+ }
+ let journeyPatterns = []
+ let page
+
+ switch (nextPage) {
+ case true:
+ page = currentPage + 1
+ break
+ case false:
+ if(currentPage > 1){
+ page = currentPage - 1
+ }
+ break
+ default:
+ page = currentPage
+ break
+ }
+ let str = ".json"
+ if(page > 1){
+ str = '.json?page=' + page.toString()
+ }
+ let urlJSON = window.location.pathname + str
+ let hasError = false
+ fetch(urlJSON, {
+ credentials: 'same-origin',
+ }).then(response => {
+ if(response.status == 500) {
+ hasError = true
+ }
+ return response.json()
+ }).then((json) => {
+ if(hasError == true) {
+ dispatch(actions.unavailableServer())
+ } else {
+ if(json.length != 0){
+ let val
+ for (val of json){
+ for (let stop_point of val.route_short_description.stop_points){
+ stop_point.checked = false
+ val.stop_area_short_descriptions.map((element) => {
+ if(element.stop_area_short_description.id === stop_point.id){
+ stop_point.checked = true
+ }
+ })
+ }
+ journeyPatterns.push({
+ name: val.name,
+ object_id: val.object_id,
+ published_name: val.published_name,
+ registration_number: val.registration_number,
+ stop_points: val.route_short_description.stop_points,
+ deletable: false
+ })
+ }
+ }
+ window.currentItemsLength = journeyPatterns.length
+ dispatch(actions.receiveJourneyPatterns(journeyPatterns))
+ }
+ })
+ },
+ getChecked : (jp) => {
+ return jp.filter((obj) => {
+ return obj.checked
+ })
+ }
+}
+
+export default actions \ No newline at end of file
diff --git a/app/javascript/journey_patterns/components/App.js b/app/javascript/journey_patterns/components/App.js
new file mode 100644
index 000000000..ac6214cc1
--- /dev/null
+++ b/app/javascript/journey_patterns/components/App.js
@@ -0,0 +1,21 @@
+import React from 'react'
+import AddJourneyPattern from '../containers/AddJourneyPattern'
+import Navigate from '../containers/Navigate'
+import Modal from '../containers/Modal'
+import ConfirmModal from '../containers/ConfirmModal'
+import SaveJourneyPattern from '../containers/SaveJourneyPattern'
+import JourneyPatternList from '../containers/JourneyPatternList'
+
+const App = () => (
+ <div>
+ <Navigate />
+ <JourneyPatternList />
+ <Navigate />
+ <AddJourneyPattern />
+ <SaveJourneyPattern />
+ <ConfirmModal />
+ <Modal/>
+ </div>
+)
+
+export default App
diff --git a/app/javascript/journey_patterns/components/ConfirmModal.js b/app/javascript/journey_patterns/components/ConfirmModal.js
new file mode 100644
index 000000000..2cc1bef44
--- /dev/null
+++ b/app/javascript/journey_patterns/components/ConfirmModal.js
@@ -0,0 +1,46 @@
+import React, { PropTypes } from 'react'
+
+export default function ConfirmModal({dispatch, modal, onModalAccept, onModalCancel, journeyPatterns}) {
+ return (
+ <div className={'modal fade ' + ((modal.type == 'confirm') ? 'in' : '')} id='ConfirmModal'>
+ <div className='modal-container'>
+ <div className='modal-dialog'>
+ <div className='modal-content'>
+ <div className='modal-header'>
+ <h4 className='modal-title'>Confirmation</h4>
+ </div>
+ <div className='modal-body'>
+ <div className='mt-md mb-md'>
+ <p>Vous vous apprêtez à changer de page. Voulez-vous valider vos modifications avant cela ?</p>
+ </div>
+ </div>
+ <div className='modal-footer'>
+ <button
+ className='btn btn-link'
+ data-dismiss='modal'
+ type='button'
+ onClick={() => { onModalCancel(modal.confirmModal.callback) }}
+ >
+ Ne pas valider
+ </button>
+ <button
+ className='btn btn-primary'
+ data-dismiss='modal'
+ type='button'
+ onClick={() => { onModalAccept(modal.confirmModal.callback, journeyPatterns) }}
+ >
+ Valider
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ )
+}
+
+ConfirmModal.propTypes = {
+ modal: PropTypes.object.isRequired,
+ onModalAccept: PropTypes.func.isRequired,
+ onModalCancel: PropTypes.func.isRequired
+} \ No newline at end of file
diff --git a/app/javascript/journey_patterns/components/CreateModal.js b/app/javascript/journey_patterns/components/CreateModal.js
new file mode 100644
index 000000000..d0eff6e57
--- /dev/null
+++ b/app/javascript/journey_patterns/components/CreateModal.js
@@ -0,0 +1,122 @@
+import React, { PropTypes, Component } from 'react'
+import actions from '../actions'
+
+export default class CreateModal extends Component {
+ constructor(props) {
+ super(props)
+ }
+
+ handleSubmit() {
+ if(actions.validateFields(this.refs) == true) {
+ this.props.onAddJourneyPattern(this.refs)
+ this.props.onModalClose()
+ $('#NewJourneyPatternModal').modal('hide')
+ }
+ }
+
+ render() {
+ if(this.props.status.isFetching == true || this.props.status.policy['journey_patterns.create'] == false || this.props.editMode == false) {
+ return false
+ }
+ if(this.props.status.fetchSuccess == true) {
+ return (
+ <div className="select_toolbox">
+ <ul>
+ <li className='st_action'>
+ <button
+ type='button'
+ data-toggle='modal'
+ data-target='#NewJourneyPatternModal'
+ onClick={this.props.onOpenCreateModal}
+ >
+ <span className="fa fa-plus"></span>
+ </button>
+
+ <div className={ 'modal fade ' + ((this.props.modal.type == 'create') ? 'in' : '') } id='NewJourneyPatternModal'>
+ <div className='modal-container'>
+ <div className='modal-dialog'>
+ <div className='modal-content'>
+ <div className='modal-header'>
+ <h4 className='modal-title'>Ajouter une mission</h4>
+ </div>
+
+ {(this.props.modal.type == 'create') && (
+ <form>
+ <div className='modal-body'>
+ <div className='form-group'>
+ <label className='control-label is-required'>Nom</label>
+ <input
+ type='text'
+ ref='name'
+ className='form-control'
+ onKeyDown={(e) => actions.resetValidation(e.currentTarget)}
+ required
+ />
+ </div>
+ <div className='row'>
+ <div className='col-lg-6 col-md-6 col-sm-6 col-xs-6'>
+ <div className='form-group'>
+ <label className='control-label is-required'>Nom public</label>
+ <input
+ type='text'
+ ref='published_name'
+ className='form-control'
+ onKeyDown={(e) => actions.resetValidation(e.currentTarget)}
+ required
+ />
+ </div>
+ </div>
+ <div className='col-lg-6 col-md-6 col-sm-6 col-xs-6'>
+ <div className='form-group'>
+ <label className='control-label'>Code mission</label>
+ <input
+ type='text'
+ ref='registration_number'
+ className='form-control'
+ onKeyDown={(e) => actions.resetValidation(e.currentTarget)}
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+ <div className='modal-footer'>
+ <button
+ className='btn btn-link'
+ data-dismiss='modal'
+ type='button'
+ onClick={this.props.onModalClose}
+ >
+ Annuler
+ </button>
+ <button
+ className='btn btn-primary'
+ type='button'
+ onClick={this.handleSubmit.bind(this)}
+ >
+ Valider
+ </button>
+ </div>
+ </form>
+ )}
+ </div>
+ </div>
+ </div>
+ </div>
+ </li>
+ </ul>
+ </div>
+ )
+ } else {
+ return false
+ }
+ }
+}
+
+CreateModal.propTypes = {
+ index: PropTypes.number,
+ modal: PropTypes.object.isRequired,
+ status: PropTypes.object.isRequired,
+ onOpenCreateModal: PropTypes.func.isRequired,
+ onModalClose: PropTypes.func.isRequired,
+ onAddJourneyPattern: PropTypes.func.isRequired
+} \ No newline at end of file
diff --git a/app/javascript/journey_patterns/components/EditModal.js b/app/javascript/journey_patterns/components/EditModal.js
new file mode 100644
index 000000000..699f89b85
--- /dev/null
+++ b/app/javascript/journey_patterns/components/EditModal.js
@@ -0,0 +1,110 @@
+import React, { PropTypes, Component } from 'react'
+import actions from '../actions'
+
+export default class EditModal extends Component {
+ constructor(props) {
+ super(props)
+ }
+
+ handleSubmit() {
+ if(actions.validateFields(this.refs) == true) {
+ this.props.saveModal(this.props.modal.modalProps.index, this.refs)
+ $('#JourneyPatternModal').modal('hide')
+ }
+ }
+
+ render() {
+ return (
+ <div className={ 'modal fade ' + ((this.props.modal.type == 'edit') ? 'in' : '') } id='JourneyPatternModal'>
+ <div className='modal-container'>
+ <div className='modal-dialog'>
+ <div className='modal-content'>
+ <div className='modal-header'>
+ <h4 className='modal-title'>
+ Editer la mission
+ {(this.props.modal.type == 'edit') && (
+ <em> "{this.props.modal.modalProps.journeyPattern.name}"</em>
+ )}
+ </h4>
+ </div>
+
+ {(this.props.modal.type == 'edit') && (
+ <form>
+ <div className='modal-body'>
+ <div className='form-group'>
+ <label className='control-label is-required'>Nom</label>
+ <input
+ type='text'
+ ref='name'
+ className='form-control'
+ id={this.props.modal.modalProps.index}
+ defaultValue={this.props.modal.modalProps.journeyPattern.name}
+ onKeyDown={(e) => actions.resetValidation(e.currentTarget)}
+ required
+ />
+ </div>
+
+ <div className='row'>
+ <div className='col-lg-6 col-md-6 col-sm-6 col-xs-6'>
+ <div className='form-group'>
+ <label className='control-label is-required'>Nom public</label>
+ <input
+ type='text'
+ ref='published_name'
+ className='form-control'
+ id={this.props.modal.modalProps.index}
+ defaultValue={this.props.modal.modalProps.journeyPattern.published_name}
+ onKeyDown={(e) => actions.resetValidation(e.currentTarget)}
+ required
+ />
+ </div>
+ </div>
+ <div className='col-lg-6 col-md-6 col-sm-6 col-xs-6'>
+ <div className='form-group'>
+ <label className='control-label'>Code mission</label>
+ <input
+ type='text'
+ ref='registration_number'
+ className='form-control'
+ id={this.props.modal.modalProps.index}
+ defaultValue={this.props.modal.modalProps.journeyPattern.registration_number}
+ onKeyDown={(e) => actions.resetValidation(e.currentTarget)}
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div className='modal-footer'>
+ <button
+ className='btn btn-link'
+ data-dismiss='modal'
+ type='button'
+ onClick={this.props.onModalClose}
+ >
+ Annuler
+ </button>
+ <button
+ className='btn btn-primary'
+ type='button'
+ onClick={this.handleSubmit.bind(this)}
+ >
+ Valider
+ </button>
+ </div>
+ </form>
+ )}
+ </div>
+ </div>
+ </div>
+ </div>
+ )
+ }
+}
+
+EditModal.propTypes = {
+ index: PropTypes.number,
+ modal: PropTypes.object,
+ onModalClose: PropTypes.func.isRequired,
+ saveModal: PropTypes.func.isRequired
+} \ No newline at end of file
diff --git a/app/javascript/journey_patterns/components/JourneyPattern.js b/app/javascript/journey_patterns/components/JourneyPattern.js
new file mode 100644
index 000000000..dde73a957
--- /dev/null
+++ b/app/javascript/journey_patterns/components/JourneyPattern.js
@@ -0,0 +1,131 @@
+import React, { PropTypes, Component } from 'react'
+import actions from '../actions'
+
+export default class JourneyPattern extends Component{
+ constructor(props){
+ super(props)
+ this.previousCity = undefined
+ }
+
+ vehicleJourneyURL(jpOid) {
+ let routeURL = window.location.pathname.split('/', 7).join('/')
+ let vjURL = routeURL + '/vehicle_journeys?jp=' + jpOid
+
+ return (
+ <a href={vjURL}>Horaires des courses</a>
+ )
+ }
+
+ cityNameChecker(sp) {
+ let bool = false
+ if(sp.city_name != this.previousCity){
+ bool = true
+ this.previousCity = sp.city_name
+ }
+ return (
+ <div
+ className={(bool) ? 'headlined' : ''}
+ >
+ <span className='has_radio'>
+ <input
+ onChange = {(e) => this.props.onCheckboxChange(e)}
+ type='checkbox'
+ id={sp.id}
+ checked={sp.checked}
+ disabled={(this.props.value.deletable || this.props.status.policy['journey_patterns.update'] == false || this.props.editMode == false) ? 'disabled' : ''}
+ >
+ </input>
+ <span className='radio-label'></span>
+ </span>
+ </div>
+ )
+ }
+
+ getErrors(errors) {
+ let err = Object.keys(errors).map((key, index) => {
+ return (
+ <li key={index} style={{listStyleType: 'disc'}}>
+ <strong>{key}</strong> { errors[key] }
+ </li>
+ )
+ })
+
+ return (
+ <ul className="alert alert-danger">{err}</ul>
+ )
+ }
+
+ isDisabled(action) {
+ return !this.props.status.policy[`journey_patterns.${action}`] && !this.props.editMode
+ }
+
+ render() {
+ this.previousCity = undefined
+
+ return (
+ <div className={'t2e-item' + (this.props.value.deletable ? ' disabled' : '') + (this.props.value.object_id ? '' : ' to_record') + (this.props.value.errors ? ' has-error': '')}>
+ {/* Errors */}
+ {/* this.props.value.errors ? this.getErrors(this.props.value.errors) : '' */}
+
+ <div className='th'>
+ <div className='strong mb-xs'>{this.props.value.object_id ? actions.humanOID(this.props.value.object_id) : '-'}</div>
+ <div>{this.props.value.registration_number}</div>
+ <div>{actions.getChecked(this.props.value.stop_points).length} arrêt(s)</div>
+
+ <div className={this.props.value.deletable ? 'btn-group disabled' : 'btn-group'}>
+ <div
+ className={this.props.value.deletable ? 'btn dropdown-toggle disabled' : 'btn dropdown-toggle'}
+ data-toggle='dropdown'
+ >
+ <span className='fa fa-cog'></span>
+ </div>
+ <ul className='dropdown-menu'>
+ <li className={this.isDisabled('update') ? 'disabled' : ''}>
+ <button
+ type='button'
+ disabled={this.isDisabled('update')}
+ onClick={this.props.onOpenEditModal}
+ data-toggle='modal'
+ data-target='#JourneyPatternModal'
+ >
+ Editer
+ </button>
+ </li>
+ <li className={this.props.value.object_id ? '' : 'disabled'}>
+ {this.vehicleJourneyURL(this.props.value.object_id)}
+ </li>
+ <li className={'delete-action' + (this.isDisabled('destroy') ? ' disabled' : '')}>
+ <button
+ type='button'
+ disabled={this.isDisabled('destroy') ? 'disabled' : ''}
+ onClick={(e) => {
+ e.preventDefault()
+ this.props.onDeleteJourneyPattern(this.props.index)}
+ }
+ >
+ <span className='fa fa-trash'></span>Supprimer
+ </button>
+ </li>
+ </ul>
+ </div>
+ </div>
+
+ {this.props.value.stop_points.map((stopPoint, i) =>{
+ return (
+ <div key={i} className='td'>
+ {this.cityNameChecker(stopPoint)}
+ </div>
+ )
+ })}
+ </div>
+ )
+ }
+}
+
+JourneyPattern.propTypes = {
+ value: PropTypes.object,
+ index: PropTypes.number,
+ onCheckboxChange: PropTypes.func.isRequired,
+ onOpenEditModal: PropTypes.func.isRequired,
+ onDeleteJourneyPattern: PropTypes.func.isRequired
+} \ No newline at end of file
diff --git a/app/javascript/journey_patterns/components/JourneyPatterns.js b/app/javascript/journey_patterns/components/JourneyPatterns.js
new file mode 100644
index 000000000..4b2badabb
--- /dev/null
+++ b/app/javascript/journey_patterns/components/JourneyPatterns.js
@@ -0,0 +1,155 @@
+import React, { PropTypes, Component } from 'react'
+import _ from 'lodash'
+import JourneyPattern from './JourneyPattern'
+
+
+export default class JourneyPatterns extends Component {
+ constructor(props){
+ super(props)
+ this.previousCity = undefined
+ }
+ componentDidMount() {
+ this.props.onLoadFirstPage()
+ }
+ componentDidUpdate(prevProps, prevState) {
+ if(this.props.status.isFetching == false){
+ $('.table-2entries').each(function() {
+ var refH = []
+ var refCol = []
+
+ $(this).find('.t2e-head').children('div').each(function() {
+ var h = $(this).outerHeight();
+ refH.push(h)
+ });
+
+ var i = 0
+ $(this).find('.t2e-item').children('div').each(function() {
+ var h = $(this).outerHeight();
+ if(refCol.length < refH.length){
+ refCol.push(h)
+ } else {
+ if(h > refCol[i]) {
+ refCol[i] = h
+ }
+ }
+ if(i == (refH.length - 1)){
+ i = 0
+ } else {
+ i++
+ }
+ });
+
+ for(var n = 0; n < refH.length; n++) {
+ if(refCol[n] < refH[n]) {
+ refCol[n] = refH[n]
+ }
+ }
+
+ $(this).find('.th').css('height', refCol[0]);
+
+ for(var nth = 1; nth < refH.length; nth++) {
+ $(this).find('.td:nth-child('+ (nth + 1) +')').css('height', refCol[nth]);
+ }
+ });
+ }
+ }
+
+ cityNameChecker(sp) {
+ let bool = false
+ if(sp.city_name != this.previousCity){
+ bool = true
+ this.previousCity = sp.city_name
+ }
+ return (
+ <div
+ className={(bool) ? 'headlined' : ''}
+ data-headline={(bool) ? sp.city_name : ''}
+ title={sp.city_name + ' (' + sp.zip_code +')'}
+ >
+ <span><span>{sp.name}</span></span>
+ </div>
+ )
+ }
+
+ render() {
+ this.previousCity = undefined
+
+ if(this.props.status.isFetching == true) {
+ return (
+ <div className="isLoading" style={{marginTop: 80, marginBottom: 80}}>
+ <div className="loader"></div>
+ </div>
+ )
+ } else {
+ return (
+ <div className='row'>
+ <div className='col-lg-12'>
+ {(this.props.status.fetchSuccess == false) && (
+ <div className="alert alert-danger mt-sm">
+ <strong>Erreur : </strong>
+ la récupération des missions a rencontré un problème. Rechargez la page pour tenter de corriger le problème
+ </div>
+ )}
+
+ { _.some(this.props.journeyPatterns, 'errors') && (
+ <div className="alert alert-danger mt-sm">
+ <strong>Erreur : </strong>
+ {this.props.journeyPatterns.map((jp, index) =>
+ jp.errors && jp.errors.map((err, i) => {
+ return (
+ <ul key={i}>
+ <li>{err}</li>
+ </ul>
+ )
+ })
+ )}
+ </div>
+ )}
+
+ <div className={'table table-2entries mt-sm mb-sm' + ((this.props.journeyPatterns.length > 0) ? '' : ' no_result')}>
+ <div className='t2e-head w20'>
+ <div className='th'>
+ <div className='strong mb-xs'>ID Mission</div>
+ <div>Code mission</div>
+ <div>Nb arrêts</div>
+ </div>
+ {this.props.stopPointsList.map((sp, i) =>{
+ return (
+ <div key={i} className='td'>
+ {this.cityNameChecker(sp)}
+ </div>
+ )
+ })}
+ </div>
+
+ <div className='t2e-item-list w80'>
+ <div>
+ {this.props.journeyPatterns.map((journeyPattern, index) =>
+ <JourneyPattern
+ value={ journeyPattern }
+ key={ index }
+ onCheckboxChange= {(e) => this.props.onCheckboxChange(e, index)}
+ onOpenEditModal= {() => this.props.onOpenEditModal(index, journeyPattern)}
+ onDeleteJourneyPattern={() => this.props.onDeleteJourneyPattern(index)}
+ status= {this.props.status}
+ editMode= {this.props.editMode}
+ />
+ )}
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ )
+ }
+ }
+}
+
+JourneyPatterns.propTypes = {
+ journeyPatterns: PropTypes.array.isRequired,
+ stopPointsList: PropTypes.array.isRequired,
+ status: PropTypes.object.isRequired,
+ onCheckboxChange: PropTypes.func.isRequired,
+ onLoadFirstPage: PropTypes.func.isRequired,
+ onOpenEditModal: PropTypes.func.isRequired
+} \ No newline at end of file
diff --git a/app/javascript/journey_patterns/components/Navigate.js b/app/javascript/journey_patterns/components/Navigate.js
new file mode 100644
index 000000000..f2fdd668f
--- /dev/null
+++ b/app/javascript/journey_patterns/components/Navigate.js
@@ -0,0 +1,62 @@
+import React, { PropTypes, Component } from 'react'
+import actions from '../actions'
+
+export default function Navigate({ dispatch, journeyPatterns, pagination, status }) {
+ let firstPage = 1
+ let lastPage = Math.ceil(pagination.totalCount / window.journeyPatternsPerPage)
+
+ let firstItemOnPage = firstPage + (pagination.perPage * (pagination.page - firstPage))
+ let lastItemOnPage = firstItemOnPage + (pagination.perPage - firstPage)
+
+ if(status.isFetching == true) {
+ return false
+ }
+ if(status.fetchSuccess == true) {
+ return (
+ <div className='row'>
+ <div className='col-lg-12 text-right'>
+ <div className='pagination'>
+ Liste des missions {firstItemOnPage} à {(lastItemOnPage < pagination.totalCount) ? lastItemOnPage : pagination.totalCount} sur {pagination.totalCount}
+ <form className='page_links' onSubmit={e => {
+ e.preventDefault()
+ }}>
+ <button
+ onClick={e => {
+ e.preventDefault()
+ dispatch(actions.checkConfirmModal(e, actions.goToPreviousPage(dispatch, pagination), pagination.stateChanged, dispatch))
+ }}
+ type='button'
+ data-toggle=''
+ data-target='#ConfirmModal'
+ className={'previous_page' + (pagination.page == firstPage ? ' disabled' : '')}
+ disabled={(pagination.page == firstPage ? ' disabled' : '')}
+ >
+ </button>
+ <button
+ onClick={e => {
+ e.preventDefault()
+ dispatch(actions.checkConfirmModal(e, actions.goToNextPage(dispatch, pagination), pagination.stateChanged, dispatch))
+ }}
+ type='button'
+ data-toggle=''
+ data-target='#ConfirmModal'
+ className={'next_page' + (pagination.page == lastPage ? ' disabled' : '')}
+ disabled={(pagination.page == lastPage ? 'disabled' : '')}
+ >
+ </button>
+ </form>
+ </div>
+ </div>
+ </div>
+ )
+ } else {
+ return false
+ }
+}
+
+Navigate.propTypes = {
+ journeyPatterns: PropTypes.array.isRequired,
+ status: PropTypes.object.isRequired,
+ pagination: PropTypes.object.isRequired,
+ dispatch: PropTypes.func.isRequired
+} \ No newline at end of file
diff --git a/app/javascript/journey_patterns/components/SaveJourneyPattern.js b/app/javascript/journey_patterns/components/SaveJourneyPattern.js
new file mode 100644
index 000000000..d071fa542
--- /dev/null
+++ b/app/javascript/journey_patterns/components/SaveJourneyPattern.js
@@ -0,0 +1,39 @@
+import React, { PropTypes, Component } from 'react'
+import actions from '../actions'
+
+export default class SaveJourneyPattern extends Component {
+ constructor(props){
+ super(props)
+ }
+
+ render() {
+ if(this.props.status.policy['journey_patterns.update'] == false) {
+ return false
+ }else{
+ return (
+ <div className='row mt-md'>
+ <div className='col-lg-12 text-right'>
+ <form className='jp_collection formSubmitr ml-xs' onSubmit={e => {e.preventDefault()}}>
+ <button
+ className='btn btn-default'
+ type='button'
+ onClick={e => {
+ e.preventDefault()
+ this.props.editMode ? this.props.onSubmitJourneyPattern(this.props.dispatch, this.props.journeyPatterns) : this.props.onEnterEditMode()
+ }}
+ >
+ {this.props.editMode ? "Valider" : "Editer"}
+ </button>
+ </form>
+ </div>
+ </div>
+ )
+ }
+ }
+}
+
+SaveJourneyPattern.propTypes = {
+ journeyPatterns: PropTypes.array.isRequired,
+ status: PropTypes.object.isRequired,
+ page: PropTypes.number.isRequired
+} \ No newline at end of file
diff --git a/app/javascript/journey_patterns/containers/AddJourneyPattern.js b/app/javascript/journey_patterns/containers/AddJourneyPattern.js
new file mode 100644
index 000000000..b093fd111
--- /dev/null
+++ b/app/javascript/journey_patterns/containers/AddJourneyPattern.js
@@ -0,0 +1,30 @@
+import { connect } from 'react-redux'
+import actions from '../actions'
+import CreateModal from '../components/CreateModal'
+
+const mapStateToProps = (state) => {
+ return {
+ modal: state.modal,
+ journeyPatterns: state.journeyPatterns,
+ editMode: state.editMode,
+ status: state.status
+ }
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ onModalClose: () =>{
+ dispatch(actions.closeModal())
+ },
+ onAddJourneyPattern: (data) =>{
+ dispatch(actions.addJourneyPattern(data))
+ },
+ onOpenCreateModal: () =>{
+ dispatch(actions.openCreateModal())
+ }
+ }
+}
+
+const AddJourneyPattern = connect(mapStateToProps, mapDispatchToProps)(CreateModal)
+
+export default AddJourneyPattern
diff --git a/app/javascript/journey_patterns/containers/ConfirmModal.js b/app/javascript/journey_patterns/containers/ConfirmModal.js
new file mode 100644
index 000000000..92ce09f33
--- /dev/null
+++ b/app/javascript/journey_patterns/containers/ConfirmModal.js
@@ -0,0 +1,30 @@
+import { connect } from 'react-redux'
+import actions from '../actions'
+import ConfirmModal from '../components/ConfirmModal'
+
+const mapStateToProps = (state) => {
+ return {
+ modal: state.modal,
+ journeyPatterns: state.journeyPatterns
+ }
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ onModalAccept: (next, state) =>{
+ dispatch(actions.fetchingApi())
+ actions.submitJourneyPattern(dispatch, state, next)
+ },
+ onModalCancel: (next) =>{
+ dispatch(actions.fetchingApi())
+ dispatch(next)
+ },
+ onModalClose: () =>{
+ dispatch(actions.closeModal())
+ }
+ }
+}
+
+const ConfirmModalContainer = connect(mapStateToProps, mapDispatchToProps)(ConfirmModal)
+
+export default ConfirmModalContainer
diff --git a/app/javascript/journey_patterns/containers/JourneyPatternList.js b/app/javascript/journey_patterns/containers/JourneyPatternList.js
new file mode 100644
index 000000000..d98734407
--- /dev/null
+++ b/app/javascript/journey_patterns/containers/JourneyPatternList.js
@@ -0,0 +1,34 @@
+import { connect } from 'react-redux'
+import actions from '../actions'
+import JourneyPatterns from '../components/JourneyPatterns'
+
+const mapStateToProps = (state) => {
+ return {
+ journeyPatterns: state.journeyPatterns,
+ status: state.status,
+ editMode: state.editMode,
+ stopPointsList: state.stopPointsList
+ }
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ onLoadFirstPage: () =>{
+ dispatch(actions.fetchingApi())
+ actions.fetchJourneyPatterns(dispatch)
+ },
+ onCheckboxChange: (e, index) =>{
+ dispatch(actions.updateCheckboxValue(e, index))
+ },
+ onOpenEditModal: (index, journeyPattern) =>{
+ dispatch(actions.openEditModal(index, journeyPattern))
+ },
+ onDeleteJourneyPattern: (index) =>{
+ dispatch(actions.deleteJourneyPattern(index))
+ }
+ }
+}
+
+const JourneyPatternList = connect(mapStateToProps, mapDispatchToProps)(JourneyPatterns)
+
+export default JourneyPatternList
diff --git a/app/javascript/journey_patterns/containers/Modal.js b/app/javascript/journey_patterns/containers/Modal.js
new file mode 100644
index 000000000..ace71a857
--- /dev/null
+++ b/app/javascript/journey_patterns/containers/Modal.js
@@ -0,0 +1,26 @@
+import { connect } from 'react-redux'
+import actions from '../actions'
+import EditModal from '../components/EditModal'
+import CreateModal from '../components/CreateModal'
+
+const mapStateToProps = (state) => {
+ return {
+ modal: state.modal,
+ journeyPattern: state.journeyPattern
+ }
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ onModalClose: () =>{
+ dispatch(actions.closeModal())
+ },
+ saveModal: (index, data) =>{
+ dispatch(actions.saveModal(index, data))
+ }
+ }
+}
+
+const ModalContainer = connect(mapStateToProps, mapDispatchToProps)(EditModal, CreateModal)
+
+export default ModalContainer
diff --git a/app/javascript/journey_patterns/containers/Navigate.js b/app/javascript/journey_patterns/containers/Navigate.js
new file mode 100644
index 000000000..d34e0b4c5
--- /dev/null
+++ b/app/javascript/journey_patterns/containers/Navigate.js
@@ -0,0 +1,15 @@
+import { connect } from 'react-redux'
+import actions from '../actions'
+import NavigateComponent from '../components/Navigate'
+
+const mapStateToProps = (state) => {
+ return {
+ journeyPatterns: state.journeyPatterns,
+ status: state.status,
+ pagination: state.pagination
+ }
+}
+
+const Navigate = connect(mapStateToProps)(NavigateComponent)
+
+export default Navigate \ No newline at end of file
diff --git a/app/javascript/journey_patterns/containers/SaveJourneyPattern.js b/app/javascript/journey_patterns/containers/SaveJourneyPattern.js
new file mode 100644
index 000000000..b630c121c
--- /dev/null
+++ b/app/javascript/journey_patterns/containers/SaveJourneyPattern.js
@@ -0,0 +1,27 @@
+import { connect } from 'react-redux'
+import actions from '../actions'
+import SaveJourneyPatternComponent from '../components/SaveJourneyPattern'
+
+const mapStateToProps = (state) => {
+ return {
+ journeyPatterns: state.journeyPatterns,
+ editMode: state.editMode,
+ page: state.pagination.page,
+ status: state.status
+ }
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ onEnterEditMode: () => {
+ dispatch(actions.enterEditMode())
+ },
+ onSubmitJourneyPattern: (next, state) => {
+ actions.submitJourneyPattern(dispatch, state, next)
+ }
+ }
+}
+
+const SaveJourneyPattern = connect(mapStateToProps, mapDispatchToProps)(SaveJourneyPatternComponent)
+
+export default SaveJourneyPattern
diff --git a/app/javascript/journey_patterns/reducers/editMode.js b/app/javascript/journey_patterns/reducers/editMode.js
new file mode 100644
index 000000000..bff976804
--- /dev/null
+++ b/app/javascript/journey_patterns/reducers/editMode.js
@@ -0,0 +1,10 @@
+export default function editMode(state = {}, action ) {
+ switch (action.type) {
+ case "ENTER_EDIT_MODE":
+ return true
+ case "EXIT_EDIT_MODE":
+ return false
+ default:
+ return state
+ }
+} \ No newline at end of file
diff --git a/app/javascript/journey_patterns/reducers/index.js b/app/javascript/journey_patterns/reducers/index.js
new file mode 100644
index 000000000..2ffaf86d4
--- /dev/null
+++ b/app/javascript/journey_patterns/reducers/index.js
@@ -0,0 +1,18 @@
+import { combineReducers } from 'redux'
+import editMode from './editMode'
+import status from './status'
+import journeyPatterns from './journeyPatterns'
+import pagination from './pagination'
+import modal from './modal'
+import stopPointsList from './stopPointsList'
+
+const journeyPatternsApp = combineReducers({
+ editMode,
+ status,
+ journeyPatterns,
+ pagination,
+ stopPointsList,
+ modal
+})
+
+export default journeyPatternsApp
diff --git a/app/javascript/journey_patterns/reducers/journeyPatterns.js b/app/javascript/journey_patterns/reducers/journeyPatterns.js
new file mode 100644
index 000000000..7702e21bc
--- /dev/null
+++ b/app/javascript/journey_patterns/reducers/journeyPatterns.js
@@ -0,0 +1,90 @@
+import _ from 'lodash'
+import actions from "../actions"
+
+export default function journeyPattern(state = {}, action) {
+ switch (action.type) {
+ case 'ADD_JOURNEYPATTERN':
+ let stopPoints = window.stopPoints
+
+ if(stopPoints != undefined) {
+ stopPoints.map((s)=>{
+ s.checked = false
+ return s
+ })
+ }
+ return {
+ name: action.data.name.value,
+ published_name: action.data.published_name.value,
+ registration_number: action.data.registration_number.value,
+ stop_points: stopPoints,
+ deletable: false
+ }
+ case 'UPDATE_CHECKBOX_VALUE':
+ var updatedStopPoints = state.stop_points.map((s) => {
+ if (String(s.id) == action.id) {
+ return _.assign({}, s, {checked : !s.checked})
+ }else {
+ return s
+ }
+ })
+ return _.assign({}, state, {stop_points: updatedStopPoints})
+ default:
+ return state
+ }
+}
+
+const journeyPatterns = (state = [], action) => {
+ switch (action.type) {
+ case 'RECEIVE_JOURNEY_PATTERNS':
+ return [...action.json]
+ case 'RECEIVE_ERRORS':
+ return [...action.json]
+ case 'GO_TO_PREVIOUS_PAGE':
+ $('#ConfirmModal').modal('hide')
+ if(action.pagination.page > 1){
+ actions.fetchJourneyPatterns(action.dispatch, action.pagination.page, action.nextPage)
+ }
+ return state
+ case 'GO_TO_NEXT_PAGE':
+ $('#ConfirmModal').modal('hide')
+ if (action.pagination.totalCount - (action.pagination.page * action.pagination.perPage) > 0){
+ actions.fetchJourneyPatterns(action.dispatch, action.pagination.page, action.nextPage)
+ }
+ return state
+ case 'UPDATE_CHECKBOX_VALUE':
+ return state.map((j, i) =>{
+ if(i == action.index) {
+ return journeyPattern(j, action)
+ } else {
+ return j
+ }
+ })
+ case 'DELETE_JOURNEYPATTERN':
+ return state.map((j, i) =>{
+ if(i == action.index) {
+ return _.assign({}, j, {deletable: true})
+ } else {
+ return j
+ }
+ })
+ case 'ADD_JOURNEYPATTERN':
+ return [
+ journeyPattern(state, action),
+ ...state
+ ]
+ case 'SAVE_MODAL':
+ return state.map((j, i) =>{
+ if(i == action.index) {
+ return _.assign({}, j, {
+ name: action.data.name.value,
+ published_name: action.data.published_name.value,
+ registration_number: action.data.registration_number.value
+ })
+ } else {
+ return j
+ }
+ })
+ default:
+ return state
+ }
+} \ No newline at end of file
diff --git a/app/javascript/journey_patterns/reducers/modal.js b/app/javascript/journey_patterns/reducers/modal.js
new file mode 100644
index 000000000..0a96f1679
--- /dev/null
+++ b/app/javascript/journey_patterns/reducers/modal.js
@@ -0,0 +1,41 @@
+import _ from 'lodash'
+
+export default function modal(state = {}, action) {
+ switch (action.type) {
+ case 'OPEN_CONFIRM_MODAL':
+ $('#ConfirmModal').modal('show')
+ return _.assign({}, state, {
+ type: 'confirm',
+ confirmModal: {
+ callback: action.callback,
+ }
+ })
+ case 'EDIT_JOURNEYPATTERN_MODAL':
+ return {
+ type: 'edit',
+ modalProps: {
+ index: action.index,
+ journeyPattern: action.journeyPattern
+ },
+ confirmModal: {}
+ }
+ case 'CREATE_JOURNEYPATTERN_MODAL':
+ return {
+ type: 'create',
+ modalProps: {},
+ confirmModal: {}
+ }
+ case 'DELETE_JOURNEYPATTERN':
+ return _.assign({}, state, { type: '' })
+ case 'SAVE_MODAL':
+ return _.assign({}, state, { type: '' })
+ case 'CLOSE_MODAL':
+ return {
+ type: '',
+ modalProps: {},
+ confirmModal: {}
+ }
+ default:
+ return state
+ }
+} \ No newline at end of file
diff --git a/app/javascript/journey_patterns/reducers/pagination.js b/app/javascript/journey_patterns/reducers/pagination.js
new file mode 100644
index 000000000..01fdf21d4
--- /dev/null
+++ b/app/javascript/journey_patterns/reducers/pagination.js
@@ -0,0 +1,35 @@
+import _ from 'lodash'
+
+export default function pagination (state = {}, action) {
+ switch (action.type) {
+ case 'RECEIVE_JOURNEY_PATTERNS':
+ return _.assign({}, state, {stateChanged: false})
+ case 'GO_TO_PREVIOUS_PAGE':
+ if (action.pagination.page > 1){
+ toggleOnConfirmModal()
+ return _.assign({}, state, {page : action.pagination.page - 1, stateChanged: false})
+ }
+ return state
+ case 'GO_TO_NEXT_PAGE':
+ if (state.totalCount - (action.pagination.page * action.pagination.perPage) > 0){
+ toggleOnConfirmModal()
+ return _.assign({}, state, {page : action.pagination.page + 1, stateChanged: false})
+ }
+ return state
+ case 'UPDATE_CHECKBOX_VALUE':
+ case 'ADD_JOURNEYPATTERN':
+ case 'SAVE_MODAL':
+ toggleOnConfirmModal('modal')
+ return _.assign({}, state, {stateChanged: true})
+ case 'UPDATE_TOTAL_COUNT':
+ return _.assign({}, state, {totalCount : state.totalCount - action.diff })
+ default:
+ return state
+ }
+}
+
+const toggleOnConfirmModal = (arg = '') =>{
+ $('.confirm').each(function(){
+ $(this).data('toggle','')
+ })
+} \ No newline at end of file
diff --git a/app/javascript/journey_patterns/reducers/status.js b/app/javascript/journey_patterns/reducers/status.js
new file mode 100644
index 000000000..88c75966d
--- /dev/null
+++ b/app/javascript/journey_patterns/reducers/status.js
@@ -0,0 +1,21 @@
+import _ from 'lodash'
+import actions from '../actions'
+
+export default function status (state = {}, action) {
+ switch (action.type) {
+ case 'UNAVAILABLE_SERVER':
+ return _.assign({}, state, {fetchSuccess: false})
+ case 'FETCH_API':
+ return _.assign({}, state, {isFetching: true})
+ case 'RECEIVE_JOURNEY_PATTERNS':
+ return _.assign({}, state, {fetchSuccess: true, isFetching: false})
+ case 'RECEIVE_ERRORS':
+ return _.assign({}, state, {isFetching: false})
+ case 'ENTER_EDIT_MODE':
+ return _.assign({}, state, {editMode: true})
+ case 'EXIT_EDIT_MODE':
+ return _.assign({}, state, {editMode: false})
+ default:
+ return state
+ }
+} \ No newline at end of file
diff --git a/app/javascript/journey_patterns/reducers/stopPointsList.js b/app/javascript/journey_patterns/reducers/stopPointsList.js
new file mode 100644
index 000000000..ee5eb1a80
--- /dev/null
+++ b/app/javascript/journey_patterns/reducers/stopPointsList.js
@@ -0,0 +1,6 @@
+export default function stopPointsList (state = [], action) {
+ switch (action.type) {
+ default:
+ return state
+ }
+} \ No newline at end of file