diff options
| author | Robert | 2017-04-24 14:16:52 +0200 |
|---|---|---|
| committer | Robert | 2017-04-24 14:16:52 +0200 |
| commit | 2d19e65fa8d8ac037c7f00a638111788b7ee002c (patch) | |
| tree | 830fe41ac13743f75f2c16f41dfd1ff399b99a9b /app | |
| parent | 5003acfc533baa824fed11cf6f82f86393b3a0f6 (diff) | |
| parent | 1cbed80c913420c76ac7d3716b9d8c4bf4e14278 (diff) | |
| download | chouette-core-2d19e65fa8d8ac037c7f00a638111788b7ee002c.tar.bz2 | |
conflict resolution and asset recompilation
Diffstat (limited to 'app')
150 files changed, 3178 insertions, 635 deletions
diff --git a/app/assets/fonts/sBoiv/sboiv.svg b/app/assets/fonts/sBoiv/sboiv.svg index 76f038bd7..be313bfcc 100644 --- a/app/assets/fonts/sBoiv/sboiv.svg +++ b/app/assets/fonts/sBoiv/sboiv.svg @@ -8,6 +8,12 @@ <missing-glyph horiz-adv-x="1024" /> <glyph unicode=" " horiz-adv-x="512" d="" /> <glyph unicode="" glyph-name="update-vj" d="M1017.856 448c0 266.935-216.393 483.328-483.328 483.328v-99.84c211.677-0.291 383.197-171.81 383.488-383.46zM918.016 448c0-0.071 0-0.154 0-0.238 0-211.795-171.693-383.488-383.488-383.488-187.627 0-343.782 134.745-376.977 312.746l-102.767 2.371c29.828-240.928 233.283-425.583 479.866-425.583 266.935 0 483.328 216.393 483.328 483.328 0 3.82-0.044 7.629-0.132 11.427zM534.528 755.2c169.662 0 307.2-137.538 307.2-307.2h-307.2zM460.8 669.184c-0.283-17.134-14.098-30.949-31.205-31.232h-136.731v-136.192c0.003-0.152 0.004-0.332 0.004-0.512 0-17.249-13.983-31.232-31.232-31.232-0.001 0-0.003 0-0.004 0h-62.976c-17.31 0.288-31.232 14.388-31.232 31.74 0 0.001 0 0.003 0 0.004v136.192h-136.192c-17.249 0-31.232 13.983-31.232 31.232v62.976c0 17.249 13.983 31.232 31.232 31.232h136.192v136.704c0.283 17.134 14.098 30.949 31.205 31.232h63.003c17.249 0 31.232-13.983 31.232-31.232v-136.704h136.192c0.001 0 0.003 0 0.004 0 17.352 0 31.452-13.922 31.74-31.205z" /> +<<<<<<< HEAD <glyph unicode="" glyph-name="preparing" d="M1011.2 614.4l-80.384 80.384c-7.765 8.206-18.732 13.313-30.891 13.313-0.12 0-0.239 0-0.359-0.001-0.129 0.002-0.303 0.003-0.478 0.003-11.952 0-22.709-5.119-30.196-13.285l-56.859-56.35 142.336-142.848 56.832 54.784c7.9 7.644 12.803 18.342 12.803 30.185 0 0.188-0.001 0.376-0.004 0.563 0.035 0.625 0.055 1.389 0.055 2.159 0 12.138-4.911 23.129-12.855 31.094zM504.32 330.752v-142.848h142.848l285.184 285.696-142.336 142.336zM805.888 544.256l-185.856-185.856c-0.834-0.338-1.802-0.535-2.816-0.535s-1.982 0.196-2.868 0.553c-0.285-0.079-0.672-0.113-1.067-0.113-3.676 0-6.656 2.98-6.656 6.656 0 0.395 0.034 0.782 0.1 1.159-0.322 0.797-0.505 1.765-0.505 2.776s0.183 1.979 0.518 2.873l188.397 183.751c0.834 0.338 1.802 0.535 2.816 0.535s1.982-0.196 2.868-0.553c0.285 0.079 0.672 0.113 1.067 0.113 3.676 0 6.656-2.98 6.656-6.656 0-0.395-0.034-0.782-0.1-1.159-0.528-1.405-1.409-2.616-2.539-3.532zM592.384 231.936v44.032h-44.032v36.864l31.232 31.232 80.384-80.384-31.232-31.232zM0 239.616h416.256v-51.2h-416.256v51.2z" /> <glyph unicode="" glyph-name="current-ref" d="M530.944 258.048l-70.144 71.68c-18.016 18.227-43.018 29.516-70.656 29.516s-52.64-11.288-70.647-29.506l-30.217 30.198c18.443 18.335 29.94 43.639 30.208 71.629-0.796 26.612-12.010 50.408-29.674 67.614l-70.677 71.701c-17.884 17.891-42.593 28.957-69.888 28.957s-52.004-11.066-69.888-28.957l-51.2-51.2c-17.891-17.884-28.957-42.593-28.957-69.888s11.066-52.004 28.957-69.888l74.24-69.632c18.016-18.227 43.018-29.516 70.656-29.516s52.64 11.288 70.647 29.506l30.217-30.198c-18.443-18.335-29.94-43.639-30.208-71.629-0.004-0.365-0.006-0.735-0.006-1.106 0-25.725 9.933-49.13 26.173-66.589l70.601-71.62c17.858-17.739 42.465-28.702 69.632-28.702s51.774 10.963 69.638 28.707l51.194 51.195c18.015 17.826 29.171 42.554 29.171 69.888s-11.156 52.062-29.163 69.88zM241.152 404.992c-11.264 11.264-20.992 24.576-38.4 24.576-18.097 0-32.768-14.671-32.768-32.768 0-17.408 13.312-27.136 24.576-38.4-5.932-6.614-14.505-10.756-24.046-10.756-0.186 0-0.372 0.002-0.558 0.005-0.198-0.006-0.465-0.009-0.732-0.009-8.862 0-16.902 3.518-22.8 9.233l-71.159 71.672c-5.852 5.917-9.468 14.056-9.468 23.040s3.615 17.123 9.471 23.043l51.197 51.197c6.111 5.71 14.344 9.216 23.396 9.216 0.055 0 0.11 0 0.164 0 0.003 0 0.017 0 0.031 0 9.18 0 17.492-3.717 23.513-9.728l69.632-72.192c6.346-6.071 10.291-14.607 10.291-24.064s-3.945-17.993-10.279-24.052zM493.568 188.416c-0.074-9.019-3.78-17.157-9.725-23.037l-51.203-51.203c-5.968-5.407-13.923-8.716-22.652-8.716-0.317 0-0.632 0.004-0.947 0.013-0.18-0.007-0.447-0.010-0.714-0.010-8.862 0-16.902 3.518-22.8 9.233l-69.111 72.184c-6.346 6.071-10.291 14.607-10.291 24.064s3.945 17.993 10.279 24.052c11.276-11.252 21.004-24.564 38.412-24.564 0.612-0.041 1.328-0.064 2.048-0.064 18.097 0 32.768 14.671 32.768 32.768 0 0.023 0 0.045 0 0.068 0 17.404-13.312 27.132-24.576 38.396 5.719 7.519 14.538 12.425 24.517 12.798 0.070 0.002 0.084 0.002 0.098 0.002 9.18 0 17.492-3.717 23.513-9.728l71.168-71.168c6.006-5.864 9.731-14.041 9.731-23.089 0-0.163-0.001-0.325-0.004-0.488zM1013.76 794.624l-51.2 51.2c-6.51 6.641-15.574 10.757-25.6 10.757s-19.090-4.116-25.594-10.751l-245.766-244.23-108.032 108.544c-6.51 6.641-15.574 10.757-25.6 10.757s-19.090-4.116-25.594-10.751l-51.206-51.206c-6.641-6.51-10.757-15.574-10.757-25.6s4.116-19.090 10.751-25.594l135.686-134.15 51.2-51.2c6.51-6.641 15.574-10.757 25.6-10.757s19.090 4.116 25.594 10.751l51.206 51.206 269.312 270.336c6.641 6.51 10.757 15.574 10.757 25.6s-4.116 19.090-10.751 25.594z" /> +======= +<glyph unicode="" glyph-name="preparing" d="M1011.173 614.391l-80.38 80.38c-7.765 8.206-18.731 13.312-30.889 13.312-0.12 0-0.239 0-0.359-0.001-0.129 0.002-0.303 0.003-0.478 0.003-11.951 0-22.708-5.119-30.194-13.284l-56.856-56.347 142.328-142.84 56.829 54.781c7.9 7.644 12.802 18.341 12.802 30.183 0 0.188-0.001 0.376-0.004 0.563 0.035 0.625 0.055 1.389 0.055 2.159 0 12.137-4.911 23.128-12.854 31.092zM504.32 330.758v-142.84h142.84l285.17 285.681-142.328 142.328zM805.872 544.251l-185.847-185.847c-0.834-0.338-1.802-0.535-2.816-0.535s-1.982 0.196-2.868 0.553c-0.285-0.079-0.672-0.113-1.067-0.113-3.676 0-6.656 2.98-6.656 6.656 0 0.395 0.034 0.782 0.1 1.159-0.322 0.797-0.505 1.765-0.505 2.776s0.183 1.979 0.518 2.873l188.387 183.741c0.834 0.338 1.802 0.535 2.816 0.535s1.982-0.196 2.868-0.553c0.285 0.079 0.672 0.113 1.067 0.113 3.676 0 6.656-2.98 6.656-6.656 0-0.395-0.034-0.782-0.1-1.159-0.528-1.405-1.409-2.616-2.539-3.532zM592.38 231.948v44.030h-44.030v36.862l31.23 31.23 80.38-80.38-31.23-31.23zM0.028 239.627h416.234v-51.197h-416.234v51.197z" /> +<glyph unicode="" glyph-name="current-ref" d="M530.92 258.291l-70.054 71.588c-17.993 18.204-42.963 29.478-70.566 29.478s-52.573-11.274-70.557-29.468l-30.178 30.159c18.419 18.312 29.902 43.583 30.169 71.537-0.795 26.578-11.995 50.343-29.636 67.527l-70.586 71.609c-17.861 17.868-42.538 28.92-69.798 28.92s-51.937-11.052-69.798-28.92l-51.134-51.134c-17.868-17.861-28.92-42.538-28.92-69.798s11.052-51.937 28.92-69.798l74.145-69.543c17.993-18.204 42.963-29.478 70.566-29.478s52.573 11.274 70.557 29.468l30.178-30.159c-18.419-18.312-29.902-43.583-30.169-71.537-0.004-0.365-0.006-0.734-0.006-1.105 0-25.692 9.92-49.067 26.139-66.504l70.511-71.528c17.835-17.716 42.411-28.665 69.543-28.665s51.708 10.949 69.549 28.67l51.128 51.129c17.992 17.803 29.134 42.5 29.134 69.798s-11.142 51.995-29.126 69.791zM241.499 405.047c-11.25 11.25-20.965 24.545-38.351 24.545-18.074 0-32.726-14.652-32.726-32.726 0-17.386 13.295-27.101 24.545-38.351-5.924-6.606-14.486-10.742-24.015-10.742-0.186 0-0.372 0.002-0.557 0.005-0.198-0.006-0.464-0.009-0.731-0.009-8.851 0-16.88 3.513-22.771 9.221l-71.068 71.58c-5.845 5.909-9.456 14.038-9.456 23.010s3.61 17.101 9.459 23.013l51.131 51.131c6.103 5.703 14.326 9.204 23.366 9.204 0.055 0 0.11 0 0.164 0 0.003 0 0.017 0 0.031 0 9.168 0 17.47-3.712 23.483-9.716l69.543-72.1c6.338-6.063 10.278-14.588 10.278-24.033s-3.94-17.97-10.266-24.021zM493.592 188.748c-0.074-9.007-3.775-17.135-9.713-23.007l-51.137-51.137c-5.96-5.4-13.905-8.705-22.623-8.705-0.317 0-0.631 0.004-0.946 0.013-0.18-0.007-0.446-0.010-0.713-0.010-8.851 0-16.88 3.513-22.771 9.221l-69.022 72.092c-6.338 6.063-10.278 14.588-10.278 24.033s3.94 17.97 10.266 24.021c11.262-11.238 20.977-24.533 38.363-24.533 0.611-0.041 1.326-0.064 2.045-0.064 18.074 0 32.726 14.652 32.726 32.726 0 0.023 0 0.045 0 0.068 0 17.382-13.295 27.097-24.545 38.347 5.712 7.509 14.519 12.409 24.486 12.782 0.070 0.002 0.084 0.002 0.098 0.002 9.168 0 17.47-3.712 23.483-9.716l71.077-71.077c5.998-5.856 9.719-14.023 9.719-23.059 0-0.163-0.001-0.325-0.004-0.487zM1013.117 794.18l-51.134 51.134c-6.502 6.632-15.554 10.743-25.567 10.743s-19.066-4.111-25.561-10.737l-245.451-243.917-107.894 108.405c-6.502 6.632-15.554 10.743-25.567 10.743s-19.066-4.111-25.561-10.737l-51.14-51.14c-6.632-6.502-10.743-15.554-10.743-25.567s4.111-19.066 10.737-25.561l135.512-133.978 51.134-51.134c6.502-6.632 15.554-10.743 25.567-10.743s19.066 4.111 25.561 10.737l51.14 51.14 268.967 269.99c6.632 6.502 10.743 15.554 10.743 25.567s-4.111 19.066-10.737 25.561z" /> +<glyph unicode="" glyph-name="chrono" horiz-adv-x="931" d="M440.957-64c243.751 0 440.957 196.267 440.957 438.857 0 225.524-171.483 412.038-391.962 436.419v51.2h73.493v97.524h-244.976v-97.524h73.493v-51.2c-220.478-24.381-391.962-209.676-391.962-436.419 0-242.59 197.206-438.857 440.957-438.857zM756.974 864.917l-71.043-70.705c71.043-41.448 131.062-101.181 172.708-171.886l71.055 70.705c-44.108 69.486-104.115 128-172.72 171.886z" /> +>>>>>>> master </font></defs></svg>
\ No newline at end of file diff --git a/app/assets/javascripts/es6_browserified/journey_patterns/actions/index.js b/app/assets/javascripts/es6_browserified/journey_patterns/actions/index.js index 709686f21..54d62f999 100644 --- a/app/assets/javascripts/es6_browserified/journey_patterns/actions/index.js +++ b/app/assets/javascripts/es6_browserified/journey_patterns/actions/index.js @@ -130,9 +130,10 @@ const actions = { if(next) { dispatch(next) } else { - if(json.length != window.journeyPatternsPerPage){ - dispatch(actions.updateTotalCount(window.journeyPatternsPerPage - json.length)) + if(json.length != window.currentItemsLength){ + dispatch(actions.updateTotalCount(window.currentItemsLength - json.length)) } + window.currentItemsLength = json.length dispatch(actions.receiveJourneyPatterns(json)) } } @@ -196,6 +197,7 @@ const actions = { }) } } + window.currentItemsLength = journeyPatterns.length dispatch(actions.receiveJourneyPatterns(journeyPatterns)) } }) diff --git a/app/assets/javascripts/es6_browserified/journey_patterns/components/CreateModal.js b/app/assets/javascripts/es6_browserified/journey_patterns/components/CreateModal.js index 573ebf228..aa2d208df 100644 --- a/app/assets/javascripts/es6_browserified/journey_patterns/components/CreateModal.js +++ b/app/assets/javascripts/es6_browserified/journey_patterns/components/CreateModal.js @@ -17,7 +17,7 @@ class CreateModal extends Component { } render() { - if(this.props.status.isFetching == true) { + if(this.props.status.isFetching == true || this.props.status.policy['journey_patterns.edit'] == false) { return false } if(this.props.status.fetchSuccess == true) { diff --git a/app/assets/javascripts/es6_browserified/journey_patterns/components/JourneyPattern.js b/app/assets/javascripts/es6_browserified/journey_patterns/components/JourneyPattern.js index 3dae38d74..d9f6d5550 100644 --- a/app/assets/javascripts/es6_browserified/journey_patterns/components/JourneyPattern.js +++ b/app/assets/javascripts/es6_browserified/journey_patterns/components/JourneyPattern.js @@ -14,7 +14,7 @@ class JourneyPattern extends Component{ let vjURL = routeURL + '/vehicle_journeys?jp=' + jpOid return ( - <a href={vjURL}>Horaires des courses</a> + <a data-no-turbolink="true" href={vjURL}>Horaires des courses</a> ) } @@ -34,7 +34,7 @@ class JourneyPattern extends Component{ type='checkbox' id={sp.id} checked={sp.checked} - disabled={this.props.value.deletable ? 'disabled' : ''} + disabled={(this.props.value.deletable || this.props.status.policy['journey_patterns.edit'] == false) ? 'disabled' : ''} > </input> <span className='radio-label'></span> @@ -74,29 +74,30 @@ class JourneyPattern extends Component{ <span className='fa fa-cog'></span> </div> <ul className='dropdown-menu'> - <li className={this.props.value.deletable ? 'disabled' : ''}> - <a - href='#' + <li className={(this.props.value.deletable || this.props.status.policy['journey_patterns.edit'] == false) ? 'disabled' : ''}> + <button + type='button' onClick={this.props.onOpenEditModal} data-toggle='modal' data-target='#JourneyPatternModal' > Editer - </a> + </button> </li> <li className={this.props.value.object_id ? '' : 'disabled'}> {this.vehicleJourneyURL(this.props.value.object_id)} </li> - <li className='delete-action'> - <a - href='#' + <li className={'delete-action' + ((this.props.status.policy['journey_patterns.edit'] == false)? ' disabled' : '')}> + <button + type='button' + disabled={(this.props.status.policy['journey_patterns.edit'] == false)? 'disabled' : ''} onClick={(e) => { e.preventDefault() this.props.onDeleteJourneyPattern(this.props.index)} } > - <span className='fa fa-trash'></span>Supprimer - </a> + <span className='fa fa-trash'></span>Supprimer + </button> </li> </ul> </div> diff --git a/app/assets/javascripts/es6_browserified/journey_patterns/components/JourneyPatterns.js b/app/assets/javascripts/es6_browserified/journey_patterns/components/JourneyPatterns.js index 37a0a5126..e0557d651 100644 --- a/app/assets/javascripts/es6_browserified/journey_patterns/components/JourneyPatterns.js +++ b/app/assets/javascripts/es6_browserified/journey_patterns/components/JourneyPatterns.js @@ -116,6 +116,7 @@ class JourneyPatterns extends Component{ onCheckboxChange= {(e) => this.props.onCheckboxChange(e, index)} onOpenEditModal= {() => this.props.onOpenEditModal(index, journeyPattern)} onDeleteJourneyPattern={() => this.props.onDeleteJourneyPattern(index)} + status= {this.props.status} /> )} </div> diff --git a/app/assets/javascripts/es6_browserified/journey_patterns/components/Navigate.js b/app/assets/javascripts/es6_browserified/journey_patterns/components/Navigate.js index 3ca860e2e..5747aa5ce 100644 --- a/app/assets/javascripts/es6_browserified/journey_patterns/components/Navigate.js +++ b/app/assets/javascripts/es6_browserified/journey_patterns/components/Navigate.js @@ -31,7 +31,7 @@ let Navigate = ({ dispatch, journeyPatterns, pagination, status }) => { data-toggle='' data-target='#ConfirmModal' className={'previous_page' + (pagination.page == firstPage ? ' disabled' : '')} - disabled={'previous_page' + (pagination.page == firstPage ? ' disabled' : '')} + disabled={(pagination.page == firstPage ? ' disabled' : '')} > </button> <button @@ -43,7 +43,7 @@ let Navigate = ({ dispatch, journeyPatterns, pagination, status }) => { data-toggle='' data-target='#ConfirmModal' className={'next_page' + (pagination.page == lastPage ? ' disabled' : '')} - disabled={'next_page' + (pagination.page == lastPage ? ' disabled' : '')} + disabled={(pagination.page == lastPage ? 'disabled' : '')} > </button> </form> diff --git a/app/assets/javascripts/es6_browserified/journey_patterns/components/SaveJourneyPattern.js b/app/assets/javascripts/es6_browserified/journey_patterns/components/SaveJourneyPattern.js index 6e09430a0..93dfa8c6b 100644 --- a/app/assets/javascripts/es6_browserified/journey_patterns/components/SaveJourneyPattern.js +++ b/app/assets/javascripts/es6_browserified/journey_patterns/components/SaveJourneyPattern.js @@ -15,7 +15,7 @@ class SaveJourneyPattern extends Component{ } render() { - if(this.props.status.isFetching == true) { + if(this.props.status.isFetching == true || (this.props.status.policy['journey_patterns.edit'] == false)) { return false } if(this.props.status.fetchSuccess == true) { diff --git a/app/assets/javascripts/es6_browserified/journey_patterns/index.js b/app/assets/javascripts/es6_browserified/journey_patterns/index.js index a2e1c2fb6..b06957e0f 100644 --- a/app/assets/javascripts/es6_browserified/journey_patterns/index.js +++ b/app/assets/javascripts/es6_browserified/journey_patterns/index.js @@ -13,6 +13,7 @@ var App = require('./components/App') var initialState = { status: { + policy: window.perms, fetchSuccess: true, isFetching: false }, diff --git a/app/assets/javascripts/es6_browserified/time_tables/actions/index.js b/app/assets/javascripts/es6_browserified/time_tables/actions/index.js new file mode 100644 index 000000000..a9fbb94cf --- /dev/null +++ b/app/assets/javascripts/es6_browserified/time_tables/actions/index.js @@ -0,0 +1,195 @@ +const _ = require('lodash') + +const actions = { + strToArrayDayTypes: (str) =>{ + let weekDays = ['Di', 'Lu', 'Ma', 'Me', 'Je', 'Ve', 'Sa'] + return weekDays.map((day, i) => str.indexOf(day) !== -1) + }, + + fetchingApi: () =>({ + type: 'FETCH_API' + }), + unavailableServer: () => ({ + type: 'UNAVAILABLE_SERVER' + }), + receiveMonth: (json) => ({ + type: 'RECEIVE_MONTH', + json + }), + receiveTimeTables: (json) => ({ + type: 'RECEIVE_TIME_TABLES', + json + }), + goToPreviousPage : (dispatch, pagination) => ({ + type: 'GO_TO_PREVIOUS_PAGE', + dispatch, + pagination, + nextPage : false + }), + goToNextPage : (dispatch, pagination) => ({ + type: 'GO_TO_NEXT_PAGE', + dispatch, + pagination, + nextPage : true + }), + changePage : (dispatch, pagination, val) => ({ + type: 'CHANGE_PAGE', + dispatch, + page: val + }), + updateDayTypes: (index) => ({ + type: 'UPDATE_DAY_TYPES', + index + }), + updateComment: (comment) => ({ + type: 'UPDATE_COMMENT', + comment + }), + updateColor: (color) => ({ + type: 'UPDATE_COLOR', + color + }), + select2Tags: (selectedTag) => ({ + type: 'UPDATE_SELECT_TAG', + selectedItem: { + id: selectedTag.id, + name: selectedTag.name + } + }), + unselect2Tags: (selectedTag) => ({ + type: 'UPDATE_UNSELECT_TAG', + selectedItem: { + id: selectedTag.id, + name: selectedTag.name + } + }), + deletePeriod: (index, dayTypes) => ({ + type: 'DELETE_PERIOD', + index, + dayTypes + }), + openAddPeriodForm: () => ({ + type: 'OPEN_ADD_PERIOD_FORM' + }), + openEditPeriodForm: (period, index) => ({ + type: 'OPEN_EDIT_PERIOD_FORM', + period, + index + }), + closePeriodForm: () => ({ + type: 'CLOSE_PERIOD_FORM' + }), + updatePeriodForm: (val, group, selectType) => ({ + type: 'UPDATE_PERIOD_FORM', + val, + group, + selectType + }), + includeDateInPeriod: (index, day, dayTypes) => ({ + type: 'INCLUDE_DATE_IN_PERIOD', + index, + day, + dayTypes + }), + excludeDateFromPeriod: (index, day, dayTypes) => ({ + type: 'EXCLUDE_DATE_FROM_PERIOD', + index, + day, + dayTypes + }), + + monthName(strDate) { + let monthList = ["Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"] + var date = new Date(strDate) + return monthList[date.getMonth()] + }, + getHumanDate(strDate, mLimit) { + let origin = strDate.split('-') + let D = origin[2] + let M = actions.monthName(strDate).toLowerCase() + let Y = origin[0] + + if(mLimit && M.length > mLimit) { + M = M.substr(0, mLimit) + '.' + } + + return (D + ' ' + M + ' ' + Y) + }, + + updateSynthesis: (state, daytypes) => { + let periods = state.time_table_periods + + let isInPeriod = function(d){ + let currentMonth = state.current_periode_range.split('-') + let twodigitsDay = d.mday < 10 ? ('0' + d.mday) : d.mday + let currentDate = new Date(currentMonth[0] + '-' + currentMonth[1] + '-' + twodigitsDay) + + // We compare periods & currentDate, to determine if it is included or not + let testDate = false + periods.map((p, i) => { + if(p.deleted){ + return false + } + let begin = new Date(p.period_start) + let end = new Date(p.period_end) + + if(testDate === false){ + if(currentDate >= begin && currentDate <= end) { + if(daytypes[d.wday] === false) { + testDate = false + } else { + testDate = true + } + } + } + }) + return testDate + } + + let improvedCM = state.current_month.map((d, i) => { + return _.assign({}, state.current_month[i], { + in_periods: isInPeriod(state.current_month[i]) + }) + }) + return improvedCM + }, + + checkConfirmModal: (event, callback, stateChanged,dispatch) => { + if(stateChanged === true){ + return actions.openConfirmModal(callback) + }else{ + dispatch(actions.fetchingApi()) + return callback + } + }, + fetchTimeTables: (dispatch, nextPage) => { + let urlJSON = window.location.pathname.split('/', 5).join('/') + // console.log(nextPage) + if(nextPage) { + urlJSON += "/month.json?date=" + nextPage + }else{ + urlJSON += ".json" + } + 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(nextPage){ + dispatch(actions.receiveMonth(json)) + }else{ + dispatch(actions.receiveTimeTables(json)) + } + } + }) + }, +} + +module.exports = actions diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/ExceptionsInDay.js b/app/assets/javascripts/es6_browserified/time_tables/components/ExceptionsInDay.js new file mode 100644 index 000000000..13615a6ef --- /dev/null +++ b/app/assets/javascripts/es6_browserified/time_tables/components/ExceptionsInDay.js @@ -0,0 +1,65 @@ +var React = require('react') +var Component = require('react').Component +var PropTypes = require('react').PropTypes +var actions = require('../actions') + +class ExceptionsInDay extends Component { + constructor(props) { + super(props) + } + + render() { + {/* display add or remove link, only if true in daytypes */} + if(this.props.outFromDaytypes == true) { + {/* display add or remove link, according to context (presence in period, or not) */} + if(this.props.value.current_month[this.props.index].in_periods == true) { + return ( + <div className='td'> + <button + type='button' + className='btn btn-circle' + data-actiontype='remove' + onClick={(e) => { + $(e.currentTarget).toggleClass('active') + this.props.onExcludeDateFromPeriod(this.props.index, this.props.value.current_month[this.props.index], this.props.metas.day_types) + }} + > + <span className='fa fa-times'></span> + </button> + </div> + ) + } else { + return ( + <div className='td'> + <button + type='button' + className='btn btn-circle' + data-actiontype='add' + onClick={(e) => { + $(e.currentTarget).toggleClass('active') + this.props.onIncludeDateInPeriod(this.props.index, this.props.value.current_month[this.props.index], this.props.metas.day_types) + }} + > + <span className='fa fa-plus'></span> + </button> + </div> + ) + } + } else { + return ( + <div className='td'></div> + ) + } + } +} + +ExceptionsInDay.propTypes = { + value: PropTypes.object.isRequired, + metas: PropTypes.object.isRequired, + outFromDaytypes: PropTypes.bool.isRequired, + onExcludeDateFromPeriod: PropTypes.func.isRequired, + onIncludeDateInPeriod: PropTypes.func.isRequired, + index: PropTypes.number.isRequired +} + +module.exports = ExceptionsInDay diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/Metas.js b/app/assets/javascripts/es6_browserified/time_tables/components/Metas.js new file mode 100644 index 000000000..943b781f5 --- /dev/null +++ b/app/assets/javascripts/es6_browserified/time_tables/components/Metas.js @@ -0,0 +1,139 @@ +var React = require('react') +var PropTypes = require('react').PropTypes +let weekDays = ['D', 'L', 'Ma', 'Me', 'J', 'V', 'S'] +var TagsSelect2 = require('./TagsSelect2') + +const Metas = ({metas, onUpdateDayTypes, onUpdateComment, onUpdateColor, onSelect2Tags, onUnselect2Tags}) => { + let colorList = ["", "#9B9B9B", "#FFA070", "#C67300", "#7F551B", "#41CCE3", "#09B09C", "#3655D7", "#6321A0", "#E796C6", "#DD2DAA"] + return ( + <div className="row"> + <div className="col-lg-6 col-lg-offset-3 col-md-8 col-md-offset-2 col-sm-10 col-sm-offset-1"> + <div className='form-horizontal'> + <div className="row"> + <div className="col-lg-12"> + {/* comment (name) */} + <div className="form-group"> + <label htmlFor="" className="control-label col-sm-4 required"> + Nom <abbr title="Champ requis">*</abbr> + </label> + <div className="col-sm-8"> + <input + type='text' + className='form-control' + value={metas.comment} + onChange={(e) => (onUpdateComment(e.currentTarget.value))} + /> + </div> + </div> + + {/* color */} + <div className="form-group"> + <label htmlFor="" className="control-label col-sm-4">Couleur associée</label> + <div className="col-sm-8"> + <div className="dropdown color_selector"> + <button + type='button' + className="btn btn-default dropdown-toggle" + id='dpdwn_color' + data-toggle='dropdown' + aria-haspopup='true' + aria-expanded='true' + > + <span + className='fa fa-circle mr-xs' + style={{color: (metas.color == '') ? 'transparent' : metas.color}} + ></span> + <span className='caret'></span> + </button> + + <div className="form-group dropdown-menu" aria-labelledby='dpdwn_color'> + {colorList.map((c, i) => + <span + className="radio" + key={i} + onClick={() => {onUpdateColor(c)}} + > + <label htmlFor=""> + <input + type='radio' + className='color_selector' + value={c} + /> + <span + className='fa fa-circle' + style={{color: ((c == '') ? 'transparent' : c)}} + ></span> + </label> + </span> + )} + </div> + </div> + </div> + </div> + + {/* tags */} + {/* <div className="form-group"> + <label htmlFor="" className="control-label col-sm-4">Etiquettes</label> + <div className="col-sm-8"> + <TagsSelect2 + tags={metas.tags} + onSelect2Tags={(e) => onSelect2Tags(e)} + onUnselect2Tags={(e) => onUnselect2Tags(e)} + /> + <input type="text" value='ton papa' className='form-control'/> + </div> + </div> + */} + + {/* calendar */} + <div className="form-group"> + <label htmlFor="" className="control-label col-sm-4">Modèle de calendrier associé</label> + <div className="col-sm-8"> + <span>{metas.calendar.name}</span> + </div> + </div> + {/* day_types */} + <div className="form-group"> + <label htmlFor="" className="control-label col-sm-4"> + Journées d'applications pour les périodes ci-dessous + </label> + <div className="col-sm-8"> + <div className="form-group labelled-checkbox-group"> + {metas.day_types.map((day, i) => + <div className="lcbx-group-item" + key={i} + > + <div className="checkbox"> + <label> + <input + onChange={(e) => {onUpdateDayTypes(i)}} + id={i} + type="checkbox" + checked={day ? 'checked' : ''} + /> + <span className='lcbx-group-item-label'>{weekDays[i]}</span> + </label> + </div> + </div> + )} + </div> + </div> + </div> + </div> + </div> + </div> + </div> + </div> + ) +} + +Metas.propTypes = { + metas: PropTypes.object.isRequired, + onUpdateDayTypes: PropTypes.func.isRequired, + onUpdateColor: PropTypes.func.isRequired, + onUpdateColor: PropTypes.func.isRequired, + onSelect2Tags: PropTypes.func.isRequired, + onUnselect2Tags: PropTypes.func.isRequired +} + +module.exports = Metas diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/Navigate.js b/app/assets/javascripts/es6_browserified/time_tables/components/Navigate.js new file mode 100644 index 000000000..5db373f9c --- /dev/null +++ b/app/assets/javascripts/es6_browserified/time_tables/components/Navigate.js @@ -0,0 +1,96 @@ +var React = require('react') +var Component = require('react').Component +var PropTypes = require('react').PropTypes +var actions = require('../actions') +var _ = require('lodash') + +let Navigate = ({ dispatch, metas, timetable, pagination, status, filters}) => { + if(status.isFetching == true) { + return false + } + if(status.fetchSuccess == true) { + let pageIndex = pagination.periode_range.indexOf(pagination.currentPage) + let firstPage = pageIndex == 0 + let lastPage = pageIndex == pagination.periode_range.length - 1 + return ( + <div className="row mt-md"> + <div className="col-lg-8 col-lg-offset-2 col-md-8 col-md-offset-2 col-sm-10 col-sm-offset-1 text-right"> + <div className="pagination"> + <form className='form-inline' onSubmit={e => {e.preventDefault()}}> + {/* date selector */} + <div className="form-group"> + <div className="dropdown month_selector" style={{display: 'inline-block'}}> + <div + className='btn btn-default dropdown-toggle' + id='date_selector' + data-toggle='dropdown' + aria-haspopup='true' + aria-expanded='true' + > + {pagination.currentPage ? (actions.monthName(pagination.currentPage) + ' ' + new Date(pagination.currentPage).getFullYear()) : ''} + <span className='caret'></span> + </div> + <ul + className='dropdown-menu' + aria-labelledby='date_selector' + > + {_.map(pagination.periode_range, (month, i) => ( + <li key={i}> + <button + type='button' + value={month} + onClick={e => { + e.preventDefault() + dispatch(actions.checkConfirmModal(e, actions.changePage(dispatch, pagination, e.currentTarget.value), pagination.stateChanged, dispatch)) + }} + > + {actions.monthName(month) + ' ' + new Date(month).getFullYear()} + </button> + </li> + ))} + </ul> + </div> + </div> + + {/* prev/next */} + <div className="form-group"> + <div className="page_links"> + <button + onClick={e => { + e.preventDefault() + dispatch(actions.checkConfirmModal(e, actions.goToPreviousPage(dispatch, pagination), pagination.stateChanged, dispatch)) + }} + type='button' + data-target='#ConfirmModal' + className={(firstPage ? 'disabled ' : '') + 'previous_page'} + disabled={(firstPage ? 'disabled' : '')} + ></button> + <button + onClick={e => { + e.preventDefault() + dispatch(actions.checkConfirmModal(e, actions.goToNextPage(dispatch, pagination), pagination.stateChanged, dispatch)) + }} + type='button' + data-target='#ConfirmModal' + className={(lastPage ? 'disabled ' : '') + 'next_page'} + disabled={(lastPage ? 'disabled' : '')} + ></button> + </div> + </div> + </form> + </div> + </div> + </div> + ) + } else { + return false + } +} + +Navigate.propTypes = { + status: PropTypes.object.isRequired, + pagination: PropTypes.object.isRequired, + dispatch: PropTypes.func.isRequired +} + +module.exports = Navigate diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/PeriodForm.js b/app/assets/javascripts/es6_browserified/time_tables/components/PeriodForm.js new file mode 100644 index 000000000..274429af8 --- /dev/null +++ b/app/assets/javascripts/es6_browserified/time_tables/components/PeriodForm.js @@ -0,0 +1,90 @@ +var React = require('react') +var PropTypes = require('react').PropTypes +let monthsArray = ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'] + +const formatNumber = (val) => { + return ("0" + val).slice(-2) +} + +const makeDaysOptions = (daySelected) => { + let arr = [] + for(let i = 1; i < 32; i++) { + arr.push(<option value={formatNumber(i)} key={i}>{formatNumber(i)}</option>) + } + return arr +} + +const makeMonthsOptions = (monthSelected) => { + let arr = [] + for(let i = 1; i < 13; i++) { + arr.push(<option value={formatNumber(i)} key={i}>{monthsArray[i - 1]}</option>) + } + return arr +} + +const makeYearsOptions = (yearSelected) => { + let arr = [] + let startYear = new Date().getFullYear() - 3 + for(let i = startYear; i <= startYear + 6; i++) { + arr.push(<option key={i}>{i}</option>) + } + return arr +} + +const PeriodForm = ({modal, timetable, onOpenAddPeriodForm, onClosePeriodForm, onUpdatePeriodForm}) => ( + <div> + {modal.modalProps.active && + <div className="form-group date filter_menu-item"> + <label className="date required control-label" >Du <abbr title="Champ requis">*</abbr></label> + <div className="form-inline"> + <select value={formatNumber(modal.modalProps.begin.day)} onChange={(e) => onUpdatePeriodForm(e.currentTarget.value, 'begin', 'day')} id="q_validity_period_begin_gteq_3i" className="date required form-control"> + {makeDaysOptions(modal.modalProps.begin.day)} + </select> + <select value={formatNumber(modal.modalProps.begin.month)} onChange={(e) => onUpdatePeriodForm(e.currentTarget.value, 'begin', 'month')} id="q_validity_period_begin_gteq_2i" className="date required form-control"> + {makeMonthsOptions(modal.modalProps.begin.month)} + </select> + <select value={modal.modalProps.begin.year} onChange={(e) => onUpdatePeriodForm(e.currentTarget.value, 'begin', 'year')} id="q_validity_period_begin_gteq_1i" className="date required form-control"> + {makeYearsOptions(modal.modalProps.begin.year)} + </select> + </div> + <label className="date required control-label" >Au <abbr title="Champ requis">*</abbr></label> + <div className="form-inline"> + <select value={formatNumber(modal.modalProps.end.day)} onChange={(e) => onUpdatePeriodForm(e.currentTarget.value, 'end', 'day')} id="q_validity_period_end_gteq_3i" className="date required form-control"> + {makeDaysOptions(modal.modalProps.end.day)} + </select> + <select value={formatNumber(modal.modalProps.end.month)} onChange={(e) => onUpdatePeriodForm(e.currentTarget.value, 'end', 'month')} id="q_validity_period_end_gteq_2i" className="date required form-control"> + {makeMonthsOptions(modal.modalProps.end.month)} + </select> + <select value={modal.modalProps.end.year} onChange={(e) => onUpdatePeriodForm(e.currentTarget.value, 'end', 'year')} id="q_validity_period_end_gteq_1i" className="date required form-control"> + {makeYearsOptions(modal.modalProps.end.year)} + </select> + </div> + <div> + <button + onClick={onClosePeriodForm} + > + Annuler + </button> + <button>Valider</button> + </div> + </div> + } + {!modal.modalProps.active && + <button + onClick={onOpenAddPeriodForm} + > + Ajouter une période + </button> + } + </div> +) + +PeriodForm.propTypes = { + modal: PropTypes.object.isRequired, + onOpenAddPeriodForm: PropTypes.func.isRequired, + onClosePeriodForm: PropTypes.func.isRequired, + onUpdatePeriodForm: PropTypes.func.isRequired, + timetable: PropTypes.object.isRequired +} + +module.exports = PeriodForm diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/PeriodManager.js b/app/assets/javascripts/es6_browserified/time_tables/components/PeriodManager.js new file mode 100644 index 000000000..de3f31ee0 --- /dev/null +++ b/app/assets/javascripts/es6_browserified/time_tables/components/PeriodManager.js @@ -0,0 +1,65 @@ +var React = require('react') +var Component = require('react').Component +var PropTypes = require('react').PropTypes +var actions = require('../actions') + +class PeriodManager extends Component { + constructor(props) { + super(props) + } + + render() { + return ( + <div + className='period_manager' + id={this.props.value.id} + > + <p className='strong'> + {actions.getHumanDate(this.props.value.period_start, 3).substr(0, 7) + ' > ' + actions.getHumanDate(this.props.value.period_end, 3)} + </p> + + <div className='dropdown'> + <div + className='btn dropdown-toggle' + id='period_actions' + data-toggle='dropdown' + aria-haspopup='true' + aria-expanded='true' + > + <span className='fa fa-cog'></span> + </div> + <ul + className='dropdown-menu' + aria-labelledby='date_selector' + > + <li> + <button + type='button' + onClick={() => this.props.onOpenEditPeriodForm(this.props.value, this.props.index)} + > + Modifier + </button> + </li> + <li className='delete-action'> + <button + type='button' + onClick={() => this.props.onDeletePeriod(this.props.index, this.props.metas.day_types)} + > + <span className='fa fa-trash'></span> + Supprimer + </button> + </li> + </ul> + </div> + </div> + ) + } +} + +PeriodManager.propTypes = { + value: PropTypes.object.isRequired, + onDeletePeriod: PropTypes.func.isRequired, + onOpenEditPeriodForm: PropTypes.func.isRequired +} + +module.exports = PeriodManager diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/PeriodsInDay.js b/app/assets/javascripts/es6_browserified/time_tables/components/PeriodsInDay.js new file mode 100644 index 000000000..93a8fe433 --- /dev/null +++ b/app/assets/javascripts/es6_browserified/time_tables/components/PeriodsInDay.js @@ -0,0 +1,78 @@ +var React = require('react') +var Component = require('react').Component +var PropTypes = require('react').PropTypes +var PeriodManager = require('./PeriodManager') + +class PeriodsInDay extends Component { + constructor(props) { + super(props) + } + + isIn(date) { + let currentDate = date.getTime() + let cls = 'td' + let periods = this.props.value + + periods.map((p, i) => { + if (!p.deleted){ + let begin = new Date(p.period_start).getTime() + let end = new Date(p.period_end).getTime() + + if(currentDate >= begin && currentDate <= end) { + if(currentDate == begin) { + cls += ' in_periods start_period' + } else if(currentDate == end) { + cls += ' in_periods end_period' + } else { + cls += ' in_periods' + } + } + } + }) + return cls + } + + render() { + return ( + <div + className={this.isIn(this.props.currentDate)} + > + {this.props.value.map((p, i) => { + if(!p.deleted){ + let begin = new Date(p.period_start).getTime() + let end = new Date(p.period_end).getTime() + let d = this.props.currentDate.getTime() + + if(d >= begin && d <= end) { + if(d == begin || (this.props.currentDate.getUTCDate() == 1)) { + return ( + <PeriodManager + key={i} + index={i} + value={p} + onDeletePeriod={this.props.onDeletePeriod} + onOpenEditPeriodForm={this.props.onOpenEditPeriodForm} + metas={this.props.metas} + /> + ) + } else { + return false + } + } + }else{ + return false + } + })} + </div> + ) + } +} + +PeriodsInDay.propTypes = { + value: PropTypes.array.isRequired, + currentDate: PropTypes.object.isRequired, + index: PropTypes.number.isRequired, + onDeletePeriod: PropTypes.func.isRequired +} + +module.exports = PeriodsInDay diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/TagsSelect2.js b/app/assets/javascripts/es6_browserified/time_tables/components/TagsSelect2.js new file mode 100644 index 000000000..16ebc250a --- /dev/null +++ b/app/assets/javascripts/es6_browserified/time_tables/components/TagsSelect2.js @@ -0,0 +1,73 @@ +var _ = require('lodash') +var React = require('react') +var PropTypes = require('react').PropTypes +var Select2 = require('react-select2') + +// get JSON full path +var origin = window.location.origin +var path = window.location.pathname.split('/', 4).join('/') +var _ = require('lodash') + +class TagsSelect2 extends React.Component{ + constructor(props) { + super(props) + } + + mapKeys(array){ + return array.map((item) => + _.mapKeys(item, (v, k) => + ((k == 'name') ? 'text' : k) + ) + ) + } + + render() { + return ( + <Select2 + value={(this.props.tags.length) ? _.map(this.props.tags, 'id') : undefined} + data={(this.props.tags.length) ? this.mapKeys(this.props.tags) : undefined} + onSelect={(e) => this.props.onSelect2Tags(e)} + onUnselect={(e) => setTimeout( () => this.props.onUnselect2Tags(e, 150))} + multiple={true} + ref='tags_id' + options={{ + allowClear: true, + theme: 'bootstrap', + width: '100%', + placeholder: 'Cherchez un tag...', + ajax: { + url: origin + path + '/tags.json', + dataType: 'json', + delay: '500', + data: function(params) { + return { + tag: params.term, + }; + }, + processResults: function(data, params) { + + return { + results: data.map( + item => _.assign( + {}, + item, + {text: item.name} + ) + ) + }; + }, + cache: true + }, + minimumInputLength: 3, + templateResult: formatRepo + }} + /> + ) + } +} + +const formatRepo = (props) => { + if(props.name) return props.name +} + +module.exports = TagsSelect2 diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/TimeTableDay.js b/app/assets/javascripts/es6_browserified/time_tables/components/TimeTableDay.js new file mode 100644 index 000000000..29c894565 --- /dev/null +++ b/app/assets/javascripts/es6_browserified/time_tables/components/TimeTableDay.js @@ -0,0 +1,35 @@ +var React = require('react') +var Component = require('react').Component +var PropTypes = require('react').PropTypes + +class TimeTableDay extends Component { + constructor(props) { + super(props) + } + + render() { + return ( + <span + className={'day' + (this.props.value.wday == 0 ? ' last_wday' : '')} + data-wday={'S' + this.props.value.wnumber} + > + <span className='dayname'> + {((this.props.value.day).charAt(0) == 'm') ? (this.props.value.day).substr(0, 2) : (this.props.value.day).charAt(0)} + </span> + <span + className={'daynumber' + (((this.props.value.in_periods && this.props.dayTypeActive && !this.props.value.excluded_date) || (this.props.value.include_date && this.props.dayTypeActive)) ? ' included' : '')} + > + {this.props.value.mday} + </span> + </span> + ) + } +} + +TimeTableDay.propTypes = { + value: PropTypes.object.isRequired, + index: PropTypes.number.isRequired, + dayTypeActive: PropTypes.bool.isRequired +} + +module.exports = TimeTableDay diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/Timetable.js b/app/assets/javascripts/es6_browserified/time_tables/components/Timetable.js new file mode 100644 index 000000000..65aae0e11 --- /dev/null +++ b/app/assets/javascripts/es6_browserified/time_tables/components/Timetable.js @@ -0,0 +1,103 @@ +var React = require('react') +var Component = require('react').Component +var PropTypes = require('react').PropTypes +var TimeTableDay = require('./TimeTableDay') +var PeriodsInDay = require('./PeriodsInDay') +var ExceptionsInDay = require('./ExceptionsInDay') +var actions = require('../actions') + +class Timetable extends Component{ + constructor(props){ + super(props) + } + + currentDate(mFirstday, day) { + let currentMonth = mFirstday.split('-') + let twodigitsDay = day < 10 ? ('0' + day) : day + let currentDate = new Date(currentMonth[0] + '-' + currentMonth[1] + '-' + twodigitsDay) + + return currentDate + } + + render() { + return ( + <div className='row'> + <div className="col-lg-8 col-lg-offset-2 col-md-8 col-md-offset-2 col-sm-10 col-sm-offset-1"> + <div className="table table-2entries mb-sm"> + <div className="t2e-head w20"> + <div className="th"> + <div className="strong">Synthèse</div> + </div> + <div className="td"><span>Journées d'application</span></div> + <div className="td"><span>Périodes</span></div> + <div className="td"><span>Exceptions</span></div> + </div> + <div className="t2e-item-list w80"> + <div> + <div className="t2e-item"> + <div className="th"> + <div className="strong monthName"> + {actions.monthName(this.props.timetable.current_periode_range)} + </div> + + <div className='monthDays'> + {this.props.timetable.current_month.map((d, i) => + <TimeTableDay + key={i} + index={i} + value={d} + dayTypeActive={this.props.metas.day_types[d.wday]} + /> + )} + </div> + </div> + + {this.props.timetable.current_month.map((d, i) => + <div + key={i} + className={'td-group' + (this.props.metas.day_types[d.wday] ? '' : ' out_from_daytypes') + (d.wday == 0 ? ' last_wday' : '')} + > + {/* day_types */} + <div className="td"></div> + + {/* periods */} + <PeriodsInDay + index={i} + value={this.props.timetable.time_table_periods} + currentDate={this.currentDate(this.props.timetable.current_periode_range, d.mday)} + onDeletePeriod={this.props.onDeletePeriod} + onOpenEditPeriodForm={this.props.onOpenEditPeriodForm} + metas={this.props.metas} + /> + + {/* exceptions */} + <ExceptionsInDay + index={i} + value={this.props.timetable} + metas={this.props.metas} + outFromDaytypes={this.props.metas.day_types[d.wday]} + onExcludeDateFromPeriod={this.props.onExcludeDateFromPeriod} + onIncludeDateInPeriod={this.props.onIncludeDateInPeriod} + /> + </div> + )} + </div> + </div> + </div> + </div> + </div> + </div> + ) + } +} + +Timetable.propTypes = { + metas: PropTypes.object.isRequired, + timetable: PropTypes.object.isRequired, + status: PropTypes.object.isRequired, + onDeletePeriod: PropTypes.func.isRequired, + onExcludeDateFromPeriod: PropTypes.func.isRequired, + onIncludeDateInPeriod: PropTypes.func.isRequired +} + +module.exports = Timetable diff --git a/app/assets/javascripts/es6_browserified/time_tables/containers/App.js b/app/assets/javascripts/es6_browserified/time_tables/containers/App.js new file mode 100644 index 000000000..fede03aec --- /dev/null +++ b/app/assets/javascripts/es6_browserified/time_tables/containers/App.js @@ -0,0 +1,38 @@ +var React = require('react') +var connect = require('react-redux').connect +var Component = require('react').Component +var actions = require('../actions') +var Metas = require('./Metas') +var Timetable = require('./Timetable') +var Navigate = require('./Navigate') +var PeriodForm = require('./PeriodForm') + +class App extends Component { + componentDidMount(){ + this.props.onLoadFirstPage() + } + + render(){ + return( + <div> + <Metas /> + <Navigate /> + <Timetable /> + <PeriodForm /> + </div> + ) + } +} + +const mapDispatchToProps = (dispatch) => { + return { + onLoadFirstPage: () =>{ + dispatch(actions.fetchingApi()) + actions.fetchTimeTables(dispatch) + } + } +} + +const timeTableApp = connect(null, mapDispatchToProps)(App) + +module.exports = timeTableApp diff --git a/app/assets/javascripts/es6_browserified/time_tables/containers/Metas.js b/app/assets/javascripts/es6_browserified/time_tables/containers/Metas.js new file mode 100644 index 000000000..514ed2347 --- /dev/null +++ b/app/assets/javascripts/es6_browserified/time_tables/containers/Metas.js @@ -0,0 +1,33 @@ +var actions = require('../actions') +var connect = require('react-redux').connect +var MetasComponent = require('../components/Metas') + +const mapStateToProps = (state) => { + return { + metas: state.metas + } +} + +const mapDispatchToProps = (dispatch) => { + return { + onUpdateDayTypes: (index) => { + dispatch(actions.updateDayTypes(index)) + }, + onUpdateComment: (comment) => { + dispatch(actions.updateComment(comment)) + }, + onUpdateColor: (color) => { + dispatch(actions.updateColor(color)) + }, + onSelect2Tags: (e) => { + dispatch(actions.select2Tags(e.params.data)) + }, + onUnselect2Tags: (e) => { + dispatch(actions.unselect2Tags(e.params.data)) + } + } +} + +const Metas = connect(mapStateToProps, mapDispatchToProps)(MetasComponent) + +module.exports = Metas diff --git a/app/assets/javascripts/es6_browserified/time_tables/containers/Navigate.js b/app/assets/javascripts/es6_browserified/time_tables/containers/Navigate.js new file mode 100644 index 000000000..c70583c25 --- /dev/null +++ b/app/assets/javascripts/es6_browserified/time_tables/containers/Navigate.js @@ -0,0 +1,18 @@ +var React = require('react') +var connect = require('react-redux').connect +var actions = require('../actions') +var NavigateComponent = require('../components/Navigate') + +const mapStateToProps = (state) => { + return { + metas: state.metas, + timetable: state.timetable, + status: state.status, + pagination: state.pagination + } +} + + +const Navigate = connect(mapStateToProps)(NavigateComponent) + +module.exports = Navigate diff --git a/app/assets/javascripts/es6_browserified/time_tables/containers/PeriodForm.js b/app/assets/javascripts/es6_browserified/time_tables/containers/PeriodForm.js new file mode 100644 index 000000000..0a785c680 --- /dev/null +++ b/app/assets/javascripts/es6_browserified/time_tables/containers/PeriodForm.js @@ -0,0 +1,28 @@ +var connect = require('react-redux').connect +var PeriodFormComponent = require('../components/PeriodForm') +var actions = require('../actions') + +const mapStateToProps = (state) => { + return { + modal: state.modal, + timetable: state.timetable + } +} + +const mapDispatchToProps = (dispatch) => { + return { + onOpenAddPeriodForm: () => { + dispatch(actions.openAddPeriodForm()) + }, + onClosePeriodForm: () => { + dispatch(actions.closePeriodForm()) + }, + onUpdatePeriodForm: (val, group, selectType) => { + dispatch(actions.updatePeriodForm(val, group, selectType)) + } + } +} + +const PeriodForm = connect(mapStateToProps, mapDispatchToProps)(PeriodFormComponent) + +module.exports = PeriodForm diff --git a/app/assets/javascripts/es6_browserified/time_tables/containers/Timetable.js b/app/assets/javascripts/es6_browserified/time_tables/containers/Timetable.js new file mode 100644 index 000000000..2a17d3dea --- /dev/null +++ b/app/assets/javascripts/es6_browserified/time_tables/containers/Timetable.js @@ -0,0 +1,32 @@ +var connect = require('react-redux').connect +var TimetableComponent = require('../components/Timetable') +var actions = require('../actions') + +const mapStateToProps = (state) => { + return { + metas: state.metas, + timetable: state.timetable, + status: state.status + } +} + +const mapDispatchToProps = (dispatch) => { + return { + onDeletePeriod: (index, dayTypes) =>{ + dispatch(actions.deletePeriod(index, dayTypes)) + }, + onExcludeDateFromPeriod: (index, day, dayTypes) => { + dispatch(actions.excludeDateFromPeriod(index, day, dayTypes)) + }, + onIncludeDateInPeriod: (index, day, dayTypes) => { + dispatch(actions.includeDateInPeriod(index, day, dayTypes)) + }, + onOpenEditPeriodForm: (period, index) => { + dispatch(actions.openEditPeriodForm(period, index)) + } + } +} + +const Timetable = connect(mapStateToProps, mapDispatchToProps)(TimetableComponent) + +module.exports = Timetable diff --git a/app/assets/javascripts/es6_browserified/time_tables/index.js b/app/assets/javascripts/es6_browserified/time_tables/index.js new file mode 100644 index 000000000..69b7fdd7c --- /dev/null +++ b/app/assets/javascripts/es6_browserified/time_tables/index.js @@ -0,0 +1,69 @@ +var React = require('react') +var render = require('react-dom').render +var Provider = require('react-redux').Provider +var createStore = require('redux').createStore +var timeTablesApp = require('./reducers') +var App = require('./containers/App') + +// 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') + +var initialState = { + status: { + policy: window.perms, + fetchSuccess: true, + isFetching: false + }, + timetable: { + current_month: [], + current_periode_range: '', + periode_range: [], + time_table_periods: [] + }, + metas: { + comment: '', + day_types: [], + tags: [], + color: '', + calendar: {} + }, + pagination: { + stateChanged: false, + currentPage: '', + periode_range: [] + }, + modal: { + type: '', + modalProps: { + active: false, + begin: { + day: '01', + month: '01', + year: String(new Date().getFullYear()) + }, + end: { + day: '01', + month: '01', + year: String(new Date().getFullYear()) + } + }, + confirmModal: {} + } +} +const loggerMiddleware = createLogger() + +let store = createStore( + timeTablesApp, + initialState, + applyMiddleware(thunkMiddleware, promise, loggerMiddleware) +) + +render( + <Provider store={store}> + <App /> + </Provider>, + document.getElementById('periods') +) diff --git a/app/assets/javascripts/es6_browserified/time_tables/reducers/index.js b/app/assets/javascripts/es6_browserified/time_tables/reducers/index.js new file mode 100644 index 000000000..5b05aadda --- /dev/null +++ b/app/assets/javascripts/es6_browserified/time_tables/reducers/index.js @@ -0,0 +1,16 @@ +var combineReducers = require('redux').combineReducers +var status = require('./status') +var pagination = require('./pagination') +var modal = require('./modal') +var timetable = require('./timetable') +var metas = require('./metas') + +const timeTablesApp = combineReducers({ + timetable, + metas, + status, + pagination, + modal +}) + +module.exports = timeTablesApp diff --git a/app/assets/javascripts/es6_browserified/time_tables/reducers/metas.js b/app/assets/javascripts/es6_browserified/time_tables/reducers/metas.js new file mode 100644 index 000000000..555730908 --- /dev/null +++ b/app/assets/javascripts/es6_browserified/time_tables/reducers/metas.js @@ -0,0 +1,33 @@ +const _ = require('lodash') +const actions = require('../actions') + +const metas = (state = {}, action) => { + switch (action.type) { + case 'RECEIVE_TIME_TABLES': + return _.assign({}, state, { + comment: action.json.comment, + day_types: actions.strToArrayDayTypes(action.json.day_types), + tags: action.json.tags, + color: action.json.color, + calendar: action.json.calendar ? action.json.calendar : {name : 'Aucun'} + }) + case 'UPDATE_DAY_TYPES': + let dayTypes = state.day_types.slice(0) + dayTypes[action.index] = !dayTypes[action.index] + return _.assign({}, state, {day_types: dayTypes}) + case 'UPDATE_COMMENT': + return _.assign({}, state, {comment: action.comment}) + case 'UPDATE_COLOR': + return _.assign({}, state, {color: action.color}) + case 'UPDATE_SELECT_TAG': + let tags = [...state.tags] + tags.push(action.selectedItem) + return _.assign({}, state, {tags: tags}) + case 'UPDATE_UNSELECT_TAG': + return _.assign({}, state, {tags: _.filter(state.tags, (t) => (t.id != action.selectedItem.id))}) + default: + return state + } +} + +module.exports = metas diff --git a/app/assets/javascripts/es6_browserified/time_tables/reducers/modal.js b/app/assets/javascripts/es6_browserified/time_tables/reducers/modal.js new file mode 100644 index 000000000..e9c0c2fb9 --- /dev/null +++ b/app/assets/javascripts/es6_browserified/time_tables/reducers/modal.js @@ -0,0 +1,42 @@ +var _ = require('lodash') +let newModalProps = {} + +const modal = (state = {}, action) => { + switch (action.type) { + case 'CLOSE_PERIOD_FORM': + let emptyDate = { + begin: '', + month: '', + year: '' + } + newModalProps = _.assign({}, state.modalProps, {active: false, begin: emptyDate, end: emptyDate, index: false}) + return _.assign({}, state, {modalProps: newModalProps}) + case 'OPEN_EDIT_PERIOD_FORM': + let period_start = action.period.period_start.split('-') + let period_end = action.period.period_end.split('-') + newModalProps = JSON.parse(JSON.stringify(state.modalProps)) + + newModalProps.begin.year = period_start[0] + newModalProps.begin.month = period_start[1] + newModalProps.begin.day = period_start[2] + + newModalProps.end.year = period_end[0] + newModalProps.end.month = period_end[1] + newModalProps.end.day = period_end[2] + + newModalProps.active = true + newModalProps.index = action.index + return _.assign({}, state, {modalProps: newModalProps}) + case 'OPEN_ADD_PERIOD_FORM': + newModalProps = _.assign({}, state.modalProps, {active: true}) + return _.assign({}, state, {modalProps: newModalProps}) + case 'UPDATE_PERIOD_FORM': + newModalProps = JSON.parse(JSON.stringify(state.modalProps)) + newModalProps[action.group][action.selectType] = action.val + return _.assign({}, state, {modalProps: newModalProps}) + default: + return state + } +} + +module.exports = modal diff --git a/app/assets/javascripts/es6_browserified/time_tables/reducers/pagination.js b/app/assets/javascripts/es6_browserified/time_tables/reducers/pagination.js new file mode 100644 index 000000000..35b9b3cd8 --- /dev/null +++ b/app/assets/javascripts/es6_browserified/time_tables/reducers/pagination.js @@ -0,0 +1,30 @@ +var _ = require('lodash') + +const pagination = (state = {}, action) => { + switch (action.type) { + case 'RECEIVE_TIME_TABLES': + return _.assign({}, state, { + currentPage: action.json.current_periode_range, + periode_range: action.json.periode_range + }) + case 'GO_TO_PREVIOUS_PAGE': + case 'GO_TO_NEXT_PAGE': + let nextPage = action.nextPage ? 1 : -1 + let newPage = action.pagination.periode_range[action.pagination.periode_range.indexOf(action.pagination.currentPage) + nextPage] + toggleOnConfirmModal() + return _.assign({}, state, {currentPage : newPage, stateChanged: false}) + case 'CHANGE_PAGE': + toggleOnConfirmModal() + return _.assign({}, state, {currentPage : action.page, stateChanged: false}) + default: + return state + } +} + +const toggleOnConfirmModal = (arg = '') =>{ + $('.confirm').each(function(){ + $(this).data('toggle','') + }) +} + +module.exports = pagination diff --git a/app/assets/javascripts/es6_browserified/time_tables/reducers/status.js b/app/assets/javascripts/es6_browserified/time_tables/reducers/status.js new file mode 100644 index 000000000..fc205d0ae --- /dev/null +++ b/app/assets/javascripts/es6_browserified/time_tables/reducers/status.js @@ -0,0 +1,17 @@ +var _ = require('lodash') + +const status = (state = {}, action) => { + switch (action.type) { + case 'UNAVAILABLE_SERVER': + return _.assign({}, state, {fetchSuccess: false}) + case 'FETCH_API': + return _.assign({}, state, {isFetching: true}) + case 'RECEIVE_TIME_TABLES': + case 'RECEIVE_MONTH': + return _.assign({}, state, {fetchSuccess: true, isFetching: false}) + default: + return state + } +} + +module.exports = status diff --git a/app/assets/javascripts/es6_browserified/time_tables/reducers/timetable.js b/app/assets/javascripts/es6_browserified/time_tables/reducers/timetable.js new file mode 100644 index 000000000..b052b5fcc --- /dev/null +++ b/app/assets/javascripts/es6_browserified/time_tables/reducers/timetable.js @@ -0,0 +1,63 @@ +const _ = require('lodash') +var actions = require('../actions') +let newState = {} + +const timetable = (state = {}, action) => { + switch (action.type) { + case 'RECEIVE_TIME_TABLES': + let fetchedState = _.assign({}, state, { + current_month: action.json.current_month, + current_periode_range: action.json.current_periode_range, + periode_range: action.json.periode_range, + time_table_periods: action.json.time_table_periods + }) + return _.assign({}, fetchedState, {current_month: actions.updateSynthesis(fetchedState, actions.strToArrayDayTypes(action.json.day_types))}) + case 'RECEIVE_MONTH': + newState = _.assign({}, state, { + current_month: action.json.days + }) + return _.assign({}, newState, {current_month: actions.updateSynthesis(newState, actions.strToArrayDayTypes(action.json.day_types))}) + case 'GO_TO_PREVIOUS_PAGE': + case 'GO_TO_NEXT_PAGE': + let nextPage = action.nextPage ? 1 : -1 + let newPage = action.pagination.periode_range[action.pagination.periode_range.indexOf(action.pagination.currentPage) + nextPage] + $('#ConfirmModal').modal('hide') + actions.fetchTimeTables(action.dispatch, newPage) + return _.assign({}, state, {current_periode_range: newPage}) + case 'CHANGE_PAGE': + $('#ConfirmModal').modal('hide') + actions.fetchTimeTables(action.dispatch, action.page) + return _.assign({}, state, {current_periode_range: action.page}) + case 'DELETE_PERIOD': + let ttperiods = state.time_table_periods.map((period, i) =>{ + if(i == action.index){ + period.deleted = true + } + return period + }) + newState = _.assign({}, state, {time_table_periods : ttperiods}) + return _.assign({}, newState, {current_month: actions.updateSynthesis(newState, action.dayTypes)}) + case 'INCLUDE_DATE_IN_PERIOD': + let newCMi = state.current_month.map((d, i) => { + if(i == action.index){ + d.include_date = !d.include_date + } + return d + }) + newState = _.assign({}, state, {current_month: newCMi}) + return _.assign({}, newState, {current_month: actions.updateSynthesis(newState, action.dayTypes)}) + case 'EXCLUDE_DATE_FROM_PERIOD': + let newCMe = state.current_month.map((d, i) => { + if(i == action.index){ + d.excluded_date = !d.excluded_date + } + return d + }) + newState = _.assign({}, state, {current_month: newCMe}) + return _.assign({}, newState, {current_month: actions.updateSynthesis(newState, action.dayTypes)}) + default: + return state + } +} + +module.exports = timetable diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/actions/index.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/actions/index.js index 4e67482da..81bbcdbb0 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/actions/index.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/actions/index.js @@ -315,9 +315,12 @@ const actions = { selected: false, published_journey_name: val.published_journey_name || 'non renseigné', published_journey_identifier: val.published_journey_name || 'non renseigné', - company_id: val.published_journey_name || 'non renseigné' + company_id: val.published_journey_name || 'non renseigné', + transport_mode: val.route.line.transport_mode || 'non renseigné', + transport_mode: val.route.line.transport_submode || 'non renseigné' }) } + window.currentItemsLength = vehicleJourneys.length dispatch(actions.receiveVehicleJourneys(vehicleJourneys)) dispatch(actions.receiveTotalCount(json.total)) } @@ -348,9 +351,10 @@ const actions = { if(next) { dispatch(next) } else { - if(json.length != window.vehicleJourneysPerPage){ - dispatch(actions.updateTotalCount(window.vehicleJourneysPerPage - json.length)) + if(json.length != window.currentItemsLength){ + dispatch(actions.updateTotalCount(window.currentItemsLength - json.length)) } + window.currentItemsLength = json.length dispatch(actions.receiveVehicleJourneys(json)) } } @@ -362,13 +366,29 @@ const actions = { return obj.selected }) }, - pad: (d) => { + simplePad: (d) => { if(d.toString().length == 1){ return (d < 10) ? '0' + d.toString() : d.toString(); }else{ return d.toString() } }, + pad: (d, timeUnit) => { + let val = d.toString() + if(d.toString().length == 1){ + val = (d < 10) ? '0' + d.toString() : d.toString(); + } + if(val.length > 2){ + val = val.substr(1) + } + if(timeUnit == 'minute' && parseInt(val) > 59){ + val = '59' + } + if(timeUnit == 'hour' && parseInt(val) > 23){ + val = '23' + } + return val + }, encodeParams: (params) => { let esc = encodeURIComponent let queryString = Object.keys(params).map((k) => esc(k) + '=' + esc(params[k])).join('&') @@ -390,20 +410,61 @@ const actions = { return vjas }, checkSchedules: (schedule) => { + let hours = 0 + let minutes = 0 if (parseInt(schedule.departure_time.minute) > 59){ - schedule.departure_time.minute = actions.pad(parseInt(schedule.departure_time.minute) - 60) - schedule.departure_time.hour = actions.pad(parseInt(schedule.departure_time.hour) + 1) + hours = Math.floor(parseInt(schedule.departure_time.minute) / 60) + minutes = parseInt(schedule.departure_time.minute) % 60 + schedule.departure_time.minute = actions.simplePad(minutes, 'minute') + schedule.departure_time.hour = parseInt(schedule.departure_time.hour) + hours } if (parseInt(schedule.arrival_time.minute) > 59){ - schedule.arrival_time.minute = actions.pad(parseInt(schedule.arrival_time.minute) - 60) - schedule.arrival_time.hour = actions.pad(parseInt(schedule.arrival_time.hour) + 1) + hours = Math.floor(parseInt(schedule.arrival_time.minute) / 60) + minutes = parseInt(schedule.arrival_time.minute) % 60 + schedule.arrival_time.minute = actions.simplePad(minutes, 'minute') + schedule.arrival_time.hour = parseInt(schedule.arrival_time.hour) + hours } - if (parseInt(schedule.departure_time.hour) > 23){ - schedule.departure_time.hour = actions.pad(parseInt(schedule.departure_time.hour) - 24) + if (parseInt(schedule.departure_time.minute) < 0){ + hours = Math.floor(parseInt(schedule.departure_time.minute) / 60) + minutes = (parseInt(schedule.departure_time.minute) % 60) + 60 + schedule.departure_time.minute = actions.simplePad(minutes, 'minute') + schedule.departure_time.hour = parseInt(schedule.departure_time.hour) + hours } - if (parseInt(schedule.arrival_time.hour) > 23){ - schedule.arrival_time.hour = actions.pad(parseInt(schedule.arrival_time.hour) - 24) + if (parseInt(schedule.arrival_time.minute) < 0){ + hours = Math.floor(parseInt(schedule.arrival_time.minute) / 60) + minutes = (parseInt(schedule.arrival_time.minute) % 60) + 60 + schedule.arrival_time.minute = actions.simplePad(minutes, 'minute') + schedule.arrival_time.hour = parseInt(schedule.arrival_time.hour) + hours } + + if(schedule.departure_time.hour > 23){ + schedule.departure_time.hour = '23' + schedule.departure_time.minute = '59' + } + if(schedule.arrival_time.hour > 23){ + schedule.arrival_time.hour = '23' + schedule.arrival_time.minute = '59' + } + + if(schedule.departure_time.hour < 0){ + schedule.departure_time.hour = '00' + schedule.departure_time.minute = '00' + } + if(schedule.arrival_time.hour > 23){ + schedule.arrival_time.hour = '00' + schedule.arrival_time.minute = '00' + } + + schedule.departure_time.hour = actions.simplePad(parseInt(schedule.departure_time.hour), 'hour') + schedule.arrival_time.hour = actions.simplePad(parseInt(schedule.arrival_time.hour), 'hour') + // if (parseInt(schedule.departure_time.hour) > 23){ + // schedule.departure_time.hour = parseInt(schedule.departure_time.hour) - 24 + // } + // if (parseInt(schedule.arrival_time.hour) > 23){ + // schedule.arrival_time.hour = parseInt(schedule.arrival_time.hour) - 24 + // } + // schedule.departure_time.hour = actions.pad(schedule.departure_time.hour, 'hour') + // schedule.arrival_time.hour = actions.pad(schedule.arrival_time.hour, 'hour') } } diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/Filters.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/Filters.js index bddb29434..6f07dd880 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/Filters.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/Filters.js @@ -97,7 +97,7 @@ const Filters = ({filters, pagination, onFilter, onResetFilters, onUpdateStartTi onChange={onToggleWithoutSchedule} checked={filters.query.withoutSchedule} ></input> - <span className='switch-label' data-checkedvalue='Oui' data-uncheckedvalue='Non'></span> + <span className='switch-label' data-checkedvalue='Non' data-uncheckedvalue='Oui'></span> </label> </div> </div> diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/Navigate.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/Navigate.js index 2d5923a4d..a62e034ae 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/Navigate.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/Navigate.js @@ -29,7 +29,7 @@ let Navigate = ({ dispatch, vehicleJourneys, pagination, status, filters}) => { type='button' data-target='#ConfirmModal' className={(pagination.page == firstPage ? 'disabled ' : '') + 'previous_page'} - disabled={(pagination.page == firstPage ? true : false)} + disabled={(pagination.page == firstPage ? 'disabled' : '')} ></button> <button onClick={e => { @@ -39,7 +39,7 @@ let Navigate = ({ dispatch, vehicleJourneys, pagination, status, filters}) => { type='button' data-target='#ConfirmModal' className={(pagination.page == lastPage ? 'disabled ' : '') + 'next_page'} - disabled={(pagination.page == lastPage ? true : false)} + disabled={(pagination.page == lastPage ? 'disabled' : '')} ></button> </form> </div> diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/SaveVehicleJourneys.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/SaveVehicleJourneys.js index b22e1d826..bd34ae114 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/SaveVehicleJourneys.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/SaveVehicleJourneys.js @@ -15,7 +15,7 @@ class SaveVehicleJourneys extends Component{ } render() { - if(this.props.status.isFetching == true) { + if(this.props.status.isFetching == true || this.props.filters.policy['vehicle_journeys.edit'] == false) { return false } if(this.props.status.fetchSuccess == true) { @@ -46,7 +46,8 @@ class SaveVehicleJourneys extends Component{ SaveVehicleJourneys.propTypes = { vehicleJourneys: PropTypes.array.isRequired, page: PropTypes.number.isRequired, - status: PropTypes.object.isRequired + status: PropTypes.object.isRequired, + filters: PropTypes.object.isRequired } module.exports = SaveVehicleJourneys diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/VehicleJourney.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/VehicleJourney.js index dc0621f76..0645fdd19 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/VehicleJourney.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/VehicleJourney.js @@ -54,7 +54,7 @@ class VehicleJourney extends Component { <div key={i}>{this.timeTableURL(tt.id)}</div> )} - {this.isDisabled(this.props.filters.policy['vehicle_journeys.edit'], this.props.filters.policy['vehicle_journeys.destroy']) && + {(this.props.filters.policy['vehicle_journeys.edit'] == true) && <div className={(this.props.value.deletable ? 'disabled ' : '') + 'checkbox'}> <input id={this.props.index} @@ -73,14 +73,14 @@ class VehicleJourney extends Component { <div key={i} className='td text-center'> <div className={'cellwrap' + (vj.dummy ? ' headlined' : '') + (this.cityNameChecker(vj) ? ' headlined' : '')}> {this.props.filters.toggleArrivals && - <div data-headline='Départ à'> - <span className={((this.props.value.deletable && (!vj.dummy)) ? 'disabled ' : '') + 'input-group time'}> + <div data-headline='Arrivée à'> + <span className={((this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.edit'] == false) ? 'disabled ' : '') + 'input-group time'}> <input type='number' min='00' max='23' className='form-control' - disabled={(this.props.value.deletable && (!vj.dummy))} + disabled={(this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.edit'] == false)} onChange={(e) => {this.props.onUpdateTime(e, i, this.props.index, 'hour', false, false)}} value={vj.arrival_time['hour']} /> @@ -90,7 +90,7 @@ class VehicleJourney extends Component { min='00' max='59' className='form-control' - disabled={((this.props.value.deletable) && (!vj.dummy))} + disabled={((this.isDisabled(this.props.value.deletable), vj.dummy) || this.props.filters.policy['vehicle_journeys.edit'] == false)} onChange={(e) => {this.props.onUpdateTime(e, i, this.props.index, 'minute', false, false)}} value={vj.arrival_time['minute']} /> @@ -102,14 +102,14 @@ class VehicleJourney extends Component { <span className='sb sb-chrono sb-lg text-warning' data-textinside={vj.delta}></span> } </div> - <div data-headline='Arrivée à'> - <span className={(this.isDisabled(this.props.value.deletable, vj.dummy) ? 'disabled ' : '') + 'input-group time'}> + <div data-headline='Départ à'> + <span className={((this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.edit'] == false) ? 'disabled ' : '') + 'input-group time'}> <input type='number' min='00' max='23' className='form-control' - disabled={this.isDisabled(this.props.value.deletable, vj.dummy)} + disabled={(this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.edit'] == false)} onChange={(e) => {this.props.onUpdateTime(e, i, this.props.index, 'hour', true, this.props.filters.toggleArrivals)}} value={vj.departure_time['hour']} /> @@ -119,7 +119,7 @@ class VehicleJourney extends Component { min='00' max='59' className='form-control' - disabled={this.isDisabled(this.props.value.deletable, vj.dummy)} + disabled={(this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.edit'] == false)} onChange={(e) => {this.props.onUpdateTime(e, i, this.props.index, "minute", true, this.props.filters.toggleArrivals)}} value={vj.departure_time['minute']} /> diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/CalendarsEditVehicleJourney.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/CalendarsEditVehicleJourney.js index 19a5af869..e32c873e6 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/CalendarsEditVehicleJourney.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/CalendarsEditVehicleJourney.js @@ -22,15 +22,15 @@ class CalendarsEditVehicleJourney extends Component { if(this.props.status.fetchSuccess == true) { return ( <li className='st_action'> - <a - href='#' - className={(actions.getSelected(this.props.vehicleJourneys).length > 0 && this.props.filters.policy['vehicle_journeys.edit']) ? '' : 'disabled'} + <button + type='button' + disabled={(actions.getSelected(this.props.vehicleJourneys).length > 0 && this.props.filters.policy['vehicle_journeys.edit']) ? '' : 'disabled'} data-toggle='modal' data-target='#CalendarsEditVehicleJourneyModal' onClick={() => this.props.onOpenCalendarsEditModal(actions.getSelected(this.props.vehicleJourneys))} > <span className='fa fa-calendar'></span> - </a> + </button> <div className={ 'modal fade ' + ((this.props.modal.type == 'duplicate') ? 'in' : '') } id='CalendarsEditVehicleJourneyModal'> <div className='modal-container'> diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/CreateModal.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/CreateModal.js index 1a1588f85..1273921e7 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/CreateModal.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/CreateModal.js @@ -25,15 +25,15 @@ class CreateModal extends Component { if(this.props.status.fetchSuccess == true) { return ( <li className='st_action'> - <a - href='#' - className={((this.props.filters.policy['vehicle_journeys.create']) ? '' : 'disabled')} + <button + type='button' + disabled={((this.props.filters.policy['vehicle_journeys.edit'] == true) ? '' : 'disabled')} data-toggle='modal' data-target='#NewVehicleJourneyModal' onClick={this.props.onOpenCreateModal} > <span className='fa fa-plus'></span> - </a> + </button> <div className={ 'modal fade ' + ((this.props.modal.type == 'create') ? 'in' : '') } id='NewVehicleJourneyModal'> <div className='modal-container'> diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/DeleteVehicleJourneys.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/DeleteVehicleJourneys.js index e2425cc22..c98b794a8 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/DeleteVehicleJourneys.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/DeleteVehicleJourneys.js @@ -5,9 +5,9 @@ var actions = require('../../actions') const DeleteVehicleJourneys = ({onDeleteVehicleJourneys, vehicleJourneys, filters}) => { return ( <li className='st_action'> - <a - href='#' - className={(actions.getSelected(vehicleJourneys).length > 0 && filters.policy['vehicle_journeys.destroy']) ? '' : 'disabled'} + <button + type='button' + disabled={(actions.getSelected(vehicleJourneys).length > 0 && filters.policy['vehicle_journeys.destroy']) ? '' : 'disabled'} onClick={e => { e.preventDefault() onDeleteVehicleJourneys() @@ -15,7 +15,7 @@ const DeleteVehicleJourneys = ({onDeleteVehicleJourneys, vehicleJourneys, filter title='Supprimer' > <span className='fa fa-trash'></span> - </a> + </button> </li> ) } diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/DuplicateVehicleJourney.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/DuplicateVehicleJourney.js index b0cb1c850..7448aa06e 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/DuplicateVehicleJourney.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/DuplicateVehicleJourney.js @@ -23,15 +23,15 @@ class DuplicateVehicleJourney extends Component { if(this.props.status.fetchSuccess == true) { return ( <li className='st_action'> - <a - href='#' - className={((actions.getSelected(this.props.vehicleJourneys).length == 1 && this.props.filters.policy['vehicle_journeys.edit']) ? '' : 'disabled')} + <button + type='button' + disabled={((actions.getSelected(this.props.vehicleJourneys).length == 1 && this.props.filters.policy['vehicle_journeys.edit']) ? '' : 'disabled')} data-toggle='modal' data-target='#DuplicateVehicleJourneyModal' onClick={this.props.onOpenDuplicateModal} > <span className='fa fa-files-o'></span> - </a> + </button> <div className={ 'modal fade ' + ((this.props.modal.type == 'duplicate') ? 'in' : '') } id='DuplicateVehicleJourneyModal'> <div className='modal-container'> @@ -68,7 +68,7 @@ class DuplicateVehicleJourney extends Component { <input type='number' ref='additional_time' - min='0' + min='-59' max='59' className='form-control' onKeyDown={(e) => actions.resetValidation(e.currentTarget)} diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/EditVehicleJourney.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/EditVehicleJourney.js index f7726dad9..9a4790051 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/EditVehicleJourney.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/EditVehicleJourney.js @@ -24,15 +24,15 @@ class EditVehicleJourney extends Component { if(this.props.status.fetchSuccess == true) { return ( <li className='st_action'> - <a - href='#' - className={(actions.getSelected(this.props.vehicleJourneys).length == 1 && this.props.filters.policy['vehicle_journeys.edit']) ? '' : 'disabled'} + <button + type='button' + disabled={(actions.getSelected(this.props.vehicleJourneys).length == 1 && this.props.filters.policy['vehicle_journeys.edit']) ? '' : 'disabled'} data-toggle='modal' data-target='#EditVehicleJourneyModal' onClick={() => this.props.onOpenEditModal(actions.getSelected(this.props.vehicleJourneys)[0])} > <span className='fa fa-info'></span> - </a> + </button> <div className={ 'modal fade ' + ((this.props.modal.type == 'duplicate') ? 'in' : '') } id='EditVehicleJourneyModal'> <div className='modal-container'> @@ -74,22 +74,51 @@ class EditVehicleJourney extends Component { <div className='row'> <div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'> - <label className='control-label is-required'>Numéro de train</label> - <input - type='text' - ref='published_journey_identifier' - className='form-control' - defaultValue={this.props.modal.modalProps.vehicleJourney.published_journey_identifier} - onKeyDown={(e) => actions.resetValidation(e.currentTarget)} - required + <div className='form-group'> + <label className='control-label is-required'>Numéro de train</label> + <input + type='text' + ref='published_journey_identifier' + className='form-control' + defaultValue={this.props.modal.modalProps.vehicleJourney.published_journey_identifier} + onKeyDown={(e) => actions.resetValidation(e.currentTarget)} + required /> + </div> </div> <div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'> - <label className='control-label'>Transporteur</label> + <div className='form-group'> + <label className='control-label'>Transporteur</label> <CompanySelect2 company = {this.props.modal.modalProps.vehicleJourney.company} onSelect2Company = {(e) => this.props.onSelect2Company(e)} /> + </div> + </div> + </div> + + <div className='row'> + <div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'> + <div className='form-group'> + <label className='control-label'>Mode de transport</label> + <input + type='text' + className='form-control' + value={(this.props.modal.modalProps.vehicleJourney.transport_mode || 'non renseigné')} + disabled={true} + /> + </div> + </div> + <div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'> + <div className='form-group'> + <label className='control-label'>Sous mode de transport</label> + <input + type='text' + className='form-control' + value={(this.props.modal.modalProps.vehicleJourney.transport_submode || 'non renseigné')} + disabled={true} + /> + </div> </div> </div> </div> diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/NotesEditVehicleJourney.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/NotesEditVehicleJourney.js index 7c5df3333..ca8b2ec7d 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/NotesEditVehicleJourney.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/NotesEditVehicleJourney.js @@ -44,15 +44,15 @@ class NotesEditVehicleJourney extends Component { if(this.props.status.fetchSuccess == true) { return ( <li className='st_action'> - <a - href='#' - className={(actions.getSelected(this.props.vehicleJourneys).length == 1 && this.props.filters.policy['vehicle_journeys.edit']) ? '' : 'disabled'} + <button + type='button' + disabled={(actions.getSelected(this.props.vehicleJourneys).length == 1 && this.props.filters.policy['vehicle_journeys.edit']) ? '' : 'disabled'} data-toggle='modal' data-target='#NotesEditVehicleJourneyModal' onClick={() => this.props.onOpenNotesEditModal(actions.getSelected(this.props.vehicleJourneys)[0])} > <span className='fa fa-sticky-note'></span> - </a> + </button> <div className={ 'modal fade ' + ((this.props.modal.type == 'duplicate') ? 'in' : '') } id='NotesEditVehicleJourneyModal'> <div className='modal-container'> diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/ShiftVehicleJourney.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/ShiftVehicleJourney.js index a373ed1e5..ee7d01cf5 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/ShiftVehicleJourney.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/ShiftVehicleJourney.js @@ -23,15 +23,15 @@ class ShiftVehicleJourney extends Component { if(this.props.status.fetchSuccess == true) { return ( <li className='st_action'> - <a - href='#' - className={(actions.getSelected(this.props.vehicleJourneys).length == 1 && this.props.filters.policy['vehicle_journeys.edit']) ? '' : 'disabled'} + <button + type='button' + disabled={(actions.getSelected(this.props.vehicleJourneys).length == 1 && this.props.filters.policy['vehicle_journeys.edit']) ? '' : 'disabled'} data-toggle='modal' data-target='#ShiftVehicleJourneyModal' onClick={this.props.onOpenShiftModal} > <span className='sb sb-update-vj'></span> - </a> + </button> <div className={ 'modal fade ' + ((this.props.modal.type == 'shift') ? 'in' : '') } id='ShiftVehicleJourneyModal'> <div className='modal-container'> @@ -54,7 +54,7 @@ class ShiftVehicleJourney extends Component { <input type='number' ref='additional_time' - min='0' + min='-59' max='59' className='form-control' onKeyDown={(e) => actions.resetValidation(e.currentTarget)} diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/CompanySelect2.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/CompanySelect2.js index 7837cdbff..1f5e5e98f 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/CompanySelect2.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/CompanySelect2.js @@ -6,6 +6,7 @@ var Select2 = require('react-select2') // get JSON full path var origin = window.location.origin var path = window.location.pathname.split('/', 3).join('/') +var line = window.location.pathname.split('/')[4] class BSelect4 extends React.Component{ @@ -27,7 +28,7 @@ class BSelect4 extends React.Component{ width: '100%', placeholder: 'Filtrer par transporteur...', ajax: { - url: origin + path + '/companies.json', + url: origin + path + '/companies.json' + '?line_id=' + line, dataType: 'json', delay: '500', data: function(params) { diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/containers/SaveVehicleJourneys.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/containers/SaveVehicleJourneys.js index 5af30ab82..87bbe5353 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/containers/SaveVehicleJourneys.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/containers/SaveVehicleJourneys.js @@ -7,7 +7,8 @@ const mapStateToProps = (state) => { return { vehicleJourneys: state.vehicleJourneys, page: state.pagination.page, - status: state.status + status: state.status, + filters: state.filters } } diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/index.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/index.js index 1b9ff8f41..4c9423c1f 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/index.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/index.js @@ -21,7 +21,7 @@ if (window.journeyPatternId) var initialState = { filters: { selectedJourneyPatterns : selectedJP, - policy: perms, + policy: window.perms, toggleArrivals: false, queryString: '', query: { @@ -44,7 +44,7 @@ var initialState = { timetable: { comment: '' }, - withoutSchedule: false + withoutSchedule: true } }, diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/filters.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/filters.js index 4b67dc4df..cd065e362 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/filters.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/filters.js @@ -15,14 +15,14 @@ const filters = (state = {}, action) => { minute: '59' } } - newQuery = _.assign({}, state.query, {interval: interval, journeyPattern: {}, timetable: {}, withoutSchedule: false }) + newQuery = _.assign({}, state.query, {interval: interval, journeyPattern: {}, timetable: {}, withoutSchedule: true }) return _.assign({}, state, {query: newQuery, queryString: ''}) case 'TOGGLE_WITHOUT_SCHEDULE': newQuery = _.assign({}, state.query, {withoutSchedule: !state.query.withoutSchedule}) return _.assign({}, state, {query: newQuery}) case 'UPDATE_END_TIME_FILTER': newInterval = JSON.parse(JSON.stringify(state.query.interval)) - newInterval.end[action.unit] = actions.pad(action.val) + newInterval.end[action.unit] = actions.pad(action.val, action.unit) if(parseInt(newInterval.start.hour + newInterval.start.minute) < parseInt(newInterval.end.hour + newInterval.end.minute)){ newQuery = _.assign({}, state.query, {interval: newInterval}) return _.assign({}, state, {query: newQuery}) @@ -31,7 +31,7 @@ const filters = (state = {}, action) => { } case 'UPDATE_START_TIME_FILTER': newInterval = JSON.parse(JSON.stringify(state.query.interval)) - newInterval.start[action.unit] = actions.pad(action.val) + newInterval.start[action.unit] = actions.pad(action.val, action.unit) if(parseInt(newInterval.start.hour + newInterval.start.minute) < parseInt(newInterval.end.hour + newInterval.end.minute)){ newQuery = _.assign({}, state.query, {interval: newInterval}) return _.assign({}, state, {query: newQuery}) @@ -54,7 +54,8 @@ const filters = (state = {}, action) => { 'q[journey_pattern_id_eq]': state.query.journeyPattern.id || undefined, 'q[time_tables_id_eq]': state.query.timetable.id || undefined, 'q[vehicle_journey_at_stops_departure_time_gteq]': (state.query.interval.start.hour + ':' + state.query.interval.start.minute), - 'q[vehicle_journey_at_stops_departure_time_lteq]': (state.query.interval.end.hour + ':' + state.query.interval.end.minute) + 'q[vehicle_journey_at_stops_departure_time_lteq]': (state.query.interval.end.hour + ':' + state.query.interval.end.minute), + 'q[vehicle_journey_without_departure_time]' : state.query.withoutSchedule } let queryString = actions.encodeParams(params) return _.assign({}, state, {queryString: queryString}) diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/vehicleJourneys.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/vehicleJourneys.js index 5924f5cc7..2db76deae 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/vehicleJourneys.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/vehicleJourneys.js @@ -47,19 +47,26 @@ const vehicleJourney= (state = {}, action) => { case 'SHIFT_VEHICLEJOURNEY': let shiftedArray, shiftedSchedule, shiftedVjas shiftedArray = state.vehicle_journey_at_stops.map((vjas, i) => { - shiftedSchedule = { - departure_time: { - hour: vjas.departure_time.hour, - minute: String(parseInt(vjas.departure_time.minute) + parseInt(action.data.additional_time.value)) - }, - arrival_time: { - hour: vjas.arrival_time.hour, - minute: String(parseInt(vjas.arrival_time.minute) + parseInt(action.data.additional_time.value)) + if (!vjas.dummy){ + shiftedSchedule = { + departure_time: { + hour: vjas.departure_time.hour, + minute: actions.simplePad(parseInt(vjas.departure_time.minute) + parseInt(action.data.additional_time.value)) + }, + arrival_time: { + hour: vjas.arrival_time.hour, + minute: actions.simplePad(parseInt(vjas.arrival_time.minute) + parseInt(action.data.additional_time.value)) + } } + actions.checkSchedules(shiftedSchedule) + shiftedVjas = _.assign({}, state.vehicle_journey_at_stops[i], shiftedSchedule) + vjas = _.assign({}, state.vehicle_journey_at_stops[i], shiftedVjas) + delete vjas['id'] + return vjas + }else { + delete vjas['id'] + return vjas } - actions.checkSchedules(shiftedSchedule) - shiftedVjas = _.assign({}, state.vehicle_journey_at_stops[i], shiftedSchedule) - return _.assign({}, state.vehicle_journey_at_stops[i], shiftedVjas) }) return _.assign({}, state, {vehicle_journey_at_stops: shiftedArray}) case 'UPDATE_TIME': @@ -71,14 +78,20 @@ const vehicleJourney= (state = {}, action) => { arrival_time: _.assign({}, vjas.arrival_time) } if (action.isDeparture){ - newSchedule.departure_time[action.timeUnit] = actions.pad(action.val) + newSchedule.departure_time[action.timeUnit] = actions.pad(action.val, action.timeUnit) if(!action.isArrivalsToggled) - newSchedule.arrival_time[action.timeUnit] = actions.pad(action.val) + newSchedule.arrival_time[action.timeUnit] = actions.pad(action.val, action.timeUnit) newSchedule = actions.getDelta(newSchedule) + if(newSchedule.delta < 0){ + return vjas + } return _.assign({}, state.vehicle_journey_at_stops[action.subIndex], {arrival_time: newSchedule.arrival_time, departure_time: newSchedule.departure_time, delta: newSchedule.delta}) }else{ - newSchedule.arrival_time[action.timeUnit] = actions.pad(action.val) + newSchedule.arrival_time[action.timeUnit] = actions.pad(action.val, action.timeUnit) newSchedule = actions.getDelta(newSchedule) + if(newSchedule.delta < 0){ + return vjas + } return _.assign({}, state.vehicle_journey_at_stops[action.subIndex], {arrival_time: newSchedule.arrival_time, departure_time: newSchedule.departure_time, delta: newSchedule.delta}) } }else{ @@ -160,11 +173,12 @@ const vehicleJourneys = (state = [], action) => { let dupeVj let dupes = [] let selectedIndex + let val = action.data.additional_time.value state.map((vj, i) => { if(vj.selected){ selectedIndex = i for (i = 0; i< action.data.duplicate_number.value; i++){ - action.data.additional_time.value *= (i + 1) + action.data.additional_time.value = val * (i + 1) dupeVj = vehicleJourney(vj, action) dupeVj.published_journey_name = dupeVj.published_journey_name + '-' + i dupeVj.selected = false diff --git a/app/assets/javascripts/forms.coffee b/app/assets/javascripts/forms.coffee index 9d884edcd..6b00e9c26 100644 --- a/app/assets/javascripts/forms.coffee +++ b/app/assets/javascripts/forms.coffee @@ -30,9 +30,21 @@ isEdge = !isIE && !!window.StyleMedia if isIE || isEdge $('.formSubmitr').off() -$(document).on 'ready page:load', togglableFilter -$(document).on 'ready page:load', submitMover -$(document).on 'ready page:load', switchInput +@colorSelector = -> + $('.form-group .dropdown.color_selector').each -> + selectedStatus = $(this).children('.dropdown-toggle').children('.fa-circle') + + $(this).on 'click', "input[type='radio']", (e) -> + selectedValue = e.currentTarget.value + if selectedValue == '' + $(selectedStatus).css('color', 'transparent') + else + $(selectedStatus).css('color', selectedValue) + +$(document).on 'turbolinks:load', togglableFilter +$(document).on 'turbolinks:load', submitMover +$(document).on 'turbolinks:load', switchInput +$(document).on 'turbolinks:load', colorSelector if isIE || isEdge $(document).on 'click', '.formSubmitr', (e)-> diff --git a/app/assets/javascripts/main_menu.coffee b/app/assets/javascripts/main_menu.coffee index 9357cff34..2e0cd3470 100644 --- a/app/assets/javascripts/main_menu.coffee +++ b/app/assets/javascripts/main_menu.coffee @@ -1,4 +1,4 @@ -$(document).on 'ready page:load', -> +$(document).on 'turbolinks:load', -> link = [] ptitleCont = "" @@ -49,5 +49,6 @@ $(document).on 'ready page:load', -> $('#main_nav').removeClass 'sticky' if $('#menu_top').find('.sticky-content').length > 0 - $('.page-action .small').after(link) + if !$('.page-action').find('.formSubmitr').length + $('.page-action .small').after(link) $('.sticky-content').remove() diff --git a/app/assets/javascripts/nav_panels.coffee b/app/assets/javascripts/nav_panels.coffee index b254b9f94..829db5ad0 100644 --- a/app/assets/javascripts/nav_panels.coffee +++ b/app/assets/javascripts/nav_panels.coffee @@ -1,4 +1,4 @@ -$(document).on 'ready page:load', -> +$(document).on 'turbolinks:load', -> $('#menu_top [data-panel="toggle"]').on 'click', (e) -> e.preventDefault() $(this).siblings().removeClass 'active' diff --git a/app/assets/javascripts/routing_constraint_zones.coffee b/app/assets/javascripts/routing_constraint_zones.coffee new file mode 100644 index 000000000..458189b36 --- /dev/null +++ b/app/assets/javascripts/routing_constraint_zones.coffee @@ -0,0 +1,27 @@ +fill_stop_points_options = -> + stop_point_select = $('#routing_constraint_zone_stop_point_ids') + stop_point_select.empty() + referential_id = document.location.pathname.match(/\d+/g)[0] + line_id = document.location.pathname.match(/\d+/g)[1] + route_id = $('#routing_constraint_zone_route_id').val() + if errors_on_form() + stop_point_ids = eval($('#stop_point_ids').val()) + $.ajax + url: "/referentials/#{referential_id}/lines/#{line_id}/routes/#{route_id}/stop_points" + dataType: 'json' + success: (data, textStatus, jqXHR) -> + for stop_point in data + selected = $.inArray(stop_point.id, stop_point_ids) != -1 + stop_point_select.append "<option value='#{stop_point.id}'" + "#{if selected then ' selected' else ''}" + ">#{stop_point.name}</option>" + error: (jqXHR, textStatus, errorThrown) -> + console.log textStatus + console.log errorThrown + +errors_on_form = -> + document.location.pathname.endsWith('routing_constraint_zones') && $('#new_routing_constraint_zone').length + +$(document).on 'turbolinks:load', -> + if document.location.pathname.endsWith('new') || errors_on_form() + fill_stop_points_options() + $('#routing_constraint_zone_route_id').change(fill_stop_points_options) + diff --git a/app/assets/javascripts/select2.coffee b/app/assets/javascripts/select2.coffee index edd4c476d..1870f7f9a 100644 --- a/app/assets/javascripts/select2.coffee +++ b/app/assets/javascripts/select2.coffee @@ -7,5 +7,13 @@ placeholder: target.data('select2ed-placeholder') allowClear: true + $('select.form-control.tags').each -> + target = $(this) + target.select2 + theme: 'bootstrap' + language: 'fr' + allowClear: true + tags: true + -$(document).on 'ready page:load', select_2 +$(document).on 'turbolinks:load', select_2 diff --git a/app/assets/javascripts/selectable_table.coffee b/app/assets/javascripts/selectable_table.coffee index 4d9f5122a..4086bf6c2 100644 --- a/app/assets/javascripts/selectable_table.coffee +++ b/app/assets/javascripts/selectable_table.coffee @@ -53,4 +53,4 @@ .addClass 'noselect' .children('.info-msg').children('span').text(selection.length) -$(document).on 'ready page:load', selectTable +$(document).on 'turbolinks:load', selectTable diff --git a/app/assets/stylesheets/application.sass b/app/assets/stylesheets/application.sass index e4a2a5445..47cccba65 100644 --- a/app/assets/stylesheets/application.sass +++ b/app/assets/stylesheets/application.sass @@ -15,3 +15,4 @@ @import 'modules/routes_stopoints' @import 'modules/jp_collection' @import 'modules/vj_collection' +@import 'modules/timetables' diff --git a/app/assets/stylesheets/base/_config.sass b/app/assets/stylesheets/base/_config.sass index ae8b386e6..65444479f 100644 --- a/app/assets/stylesheets/base/_config.sass +++ b/app/assets/stylesheets/base/_config.sass @@ -21,3 +21,4 @@ $green: #70b12b $red: #da2f36 $orange: #ed7f00 +$gold: #ffcc00 diff --git a/app/assets/stylesheets/components/_buttons.sass b/app/assets/stylesheets/components/_buttons.sass index 00551f7cb..fd3561dc8 100644 --- a/app/assets/stylesheets/components/_buttons.sass +++ b/app/assets/stylesheets/components/_buttons.sass @@ -5,15 +5,45 @@ .btn font-weight: 700 - .fa + span + .fa + span:not(.caret) padding-left: 0.5em span + .fa padding-left: 0.5em - &.btn-link + &.btn-link, &.btn-circle &, &:focus outline: none + &.btn-circle + position: relative + top: 50% + transform: translateY(-50%) + border: 1px solid $blue + background-color: transparent + width: 22px + height: 22px + line-height: 22px + text-align: center + border-radius: 50% + padding: 0 + color: $grey + + > .fa + display: block + line-height: 20px + + &[data-actiontype='add'] + &:hover, &.active + border-color: $green + background-color: $green + color: #fff + + &[data-actiontype='remove'] + &:hover, &.active + border-color: $red + background-color: $red + color: #fff + &.btn-default background-color: #fff border-color: rgba(#fff, 0.5) @@ -113,12 +143,12 @@ table, .table border-radius: 0 box-shadow: 0 0 3px rgba($darkgrey, 0.25) - > li > a + > li > a, > li > button padding: 5px 15px > li.delete-action - - > a + > a, > button + display: block position: relative margin-top: 11px diff --git a/app/assets/stylesheets/components/_calendar.sass b/app/assets/stylesheets/components/_calendar.sass new file mode 100644 index 000000000..c3d93d325 --- /dev/null +++ b/app/assets/stylesheets/components/_calendar.sass @@ -0,0 +1,73 @@ +//-----------// +// CALENDARS // +//-----------// + +table.calendar + display: table + width: 100% + table-layout: fixed + margin-bottom: 20px + + th, td + border: 1px solid $darkgrey + text-align: center + padding: 0.35em 0.5em + + th + padding: 0.5em + + thead + tr + > th + border: none + + &:first-child + text-transform: uppercase + + tbody + tr > td + &.weekNumber + border: none + padding: 0.5em 0.5em 0.5em 0 + + &.outsideMonth + color: rgba($darkgrey, 0.35) + + &.weekend + font-weight: bold + + &.selected_period, &.selected_date, &.overlaped_date + > a + display: block + margin: 0 auto + width: 24px + height: 24px + line-height: 24px + color: $darkgrey + background-color: rgba($gold, 0.75) + border-radius: 50% + + &:hover, &:focus + color: $darkgrey + text-decoration: none + +.col-xs-6 + &:nth-child(2n +1) + clear: both + +.col-sm-4 + @media (min-width: 768px) + &:nth-child(2n +1) + clear: none + &:nth-child(3n+1) + clear: both + +// .col-md-4 +// @media (min-width: 992px) + +.col-lg-3 + @media (min-width: 1200px) + &:nth-child(2n +1), &:nth-child(3n+1) + clear: none + &:nth-child(4n+1) + clear: both diff --git a/app/assets/stylesheets/components/_dropdown.sass b/app/assets/stylesheets/components/_dropdown.sass index ee26aaeb5..99dc6292e 100644 --- a/app/assets/stylesheets/components/_dropdown.sass +++ b/app/assets/stylesheets/components/_dropdown.sass @@ -3,6 +3,22 @@ //-------------// .dropdown-menu - > .disabled > a + > li > button + display: block + width: 100% + text-align: left + border: none + background-color: transparent + color: #333 + + &:hover, &:focus + color: #262626 + background-color: whitesmoke + outline: none + + > .disabled > a, > .disabled > button + cursor: not-allowed &, &:hover, &:focus color: rgba($darkgrey, 0.5) + background-color: transparent + outline: none diff --git a/app/assets/stylesheets/components/_forms.sass b/app/assets/stylesheets/components/_forms.sass index 3b7a135d5..8419c2345 100644 --- a/app/assets/stylesheets/components/_forms.sass +++ b/app/assets/stylesheets/components/_forms.sass @@ -77,6 +77,57 @@ input border-color: #4cae4c box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px #5cb85c +// BS horizontal form label positionning fix +.form-horizontal + .form-group + position: relative + + > .control-label + &[class*='col-sm-'] + float: none + padding-top: 0 + position: absolute + left: 0 + right: 0 + top: 50% + transform: translateY(-50%) + + + [class*='col-sm-'] + float: right + +// Colors dropdown +.dropdown.color_selector + .btn.btn-default + &, &:hover, &:focus + border: 1px solid #cccccc + background-color: #fff + font-weight: normal + color: $darkgrey + + .dropdown-menu + position: absolute + margin: 2px 0 0 0 + min-width: 56px + + > .radio + position: relative + padding: 3px 20px + // cursor: pointer + + &:hover, &:focus + background-color: #f5f5f5 + + > label + padding: 0 + + input.color_selector, input[type='radio'] + position: absolute + cursor: pointer + z-index: 1 + width: 100% + margin: 0 + opacity: 0 + // Search bar .search_bar position: relative @@ -646,6 +697,59 @@ table, .table .control-label.time min-width: 60px +// Form group checkboxes +$grp-cbx-size: 34px +.form-group.labelled-checkbox-group + margin: 0 + + > .lcbx-group-item + display: inline-block + margin: 0 5px + + &:first-child + margin-left: 0 + + > .checkbox + display: block + width: $grp-cbx-size + height: $grp-cbx-size + line-height: $grp-cbx-size + margin: 0 + padding: 0 + + > label + position: relative + min-height: 0 + padding: 0 + margin: 0 + + > input + &[type='checkbox']:not(:checked), &[type='checkbox']:checked + position: absolute + left: -9999px + margin: 0 + + + .lcbx-group-item-label + position: relative + cursor: pointer + font-weight: bold + display: inline-block + vertical-align: top + width: $grp-cbx-size + height: $grp-cbx-size + line-height: $grp-cbx-size - 2px + text-align: center + margin: 0 + background-color: rgba($grey, 0.15) + color: rgba($grey, 0.5) + border: 1px solid rgba($grey, 0.5) + border-radius: 3px + + &[type='checkbox']:checked + .lcbx-group-item-label + background-color: $blue + border-color: $blue + color: #fff + // Nested fields .nested-fields margin: 0 @@ -727,7 +831,7 @@ table, .table margin: 0 padding: 0 5px - + .nested-fields + + .nested-fields, + .links.nested-linker border-top: 2px solid $darkgrey .wrapper diff --git a/app/assets/stylesheets/components/_labels.sass b/app/assets/stylesheets/components/_labels.sass index 3514bce81..9607a97b8 100644 --- a/app/assets/stylesheets/components/_labels.sass +++ b/app/assets/stylesheets/components/_labels.sass @@ -8,7 +8,15 @@ font-weight: inherit padding: 0.35em 0.4em border-radius: 2px + border: 1px solid + + + .label + margin-left: 0.5em &.label-default - background-color: rgba(#fff, 0.3) - color: #fff + background-color: #fff + color: $darkgrey + border-color: rgba($grey, 0.5) + + &.disabled + color: rgba($grey, 0.5) diff --git a/app/assets/stylesheets/components/_select2.sass b/app/assets/stylesheets/components/_select2.sass index 5e3741bf0..cbb3a80da 100644 --- a/app/assets/stylesheets/components/_select2.sass +++ b/app/assets/stylesheets/components/_select2.sass @@ -50,9 +50,19 @@ .form-filter .form-group.select2ed - .select2-container--bootstrap .select2-selection--single - height: 31px - padding: 4px 24px 5px 12px + .select2-container--bootstrap + .select2-selection--single, .select2-selection--multiple + height: 31px + padding: 0px 3px 4px 6px + + .select2-selection--single + line-height: 31px + + .select2-selection--multiple + min-height: 31px + + .select2-search--inline .select2-search__field + height: 28px .select2-container--bootstrap .select2-selection border-color: rgba($grey, 0.3) diff --git a/app/assets/stylesheets/components/_tables.sass b/app/assets/stylesheets/components/_tables.sass index ade9bacf4..bc04b49e3 100644 --- a/app/assets/stylesheets/components/_tables.sass +++ b/app/assets/stylesheets/components/_tables.sass @@ -118,13 +118,15 @@ height: 35px margin: 5px - > a + > a, > button display: block height: 35px + width: 100% text-align: center line-height: 35px border-radius: 50% background-color: $blue + border: none color: #fff &:focus @@ -139,7 +141,7 @@ &:hover background-color: darken($red, 5%) - &.disabled + &.disabled, &:disabled &, &[title='Supprimer'], &:hover, &:focus background-color: rgba($grey, 0.3) cursor: not-allowed @@ -262,3 +264,11 @@ &.disabled color: rgba($darkgrey, 0.5) background-color: rgba(#fff, 0.5) + + // TD group + > .td-group + display: inline-block + vertical-align: top + + > .td + display: block diff --git a/app/assets/stylesheets/main/time_tables.sass b/app/assets/stylesheets/main/time_tables.sass index de2ae8253..6918bec1e 100644 --- a/app/assets/stylesheets/main/time_tables.sass +++ b/app/assets/stylesheets/main/time_tables.sass @@ -28,13 +28,13 @@ z-index: 100001 .validity_out - color: $brand-danger + color: red .validity_out_soon - color: $brand-warning + color: orange .validity_regular - color: $brand-success + color: green span.included_day_type font-weight: bolder diff --git a/app/assets/stylesheets/modules/_jp_collection.sass b/app/assets/stylesheets/modules/_jp_collection.sass index eff1e2abc..d1f864e5c 100644 --- a/app/assets/stylesheets/modules/_jp_collection.sass +++ b/app/assets/stylesheets/modules/_jp_collection.sass @@ -78,3 +78,7 @@ &:after bottom: -6px + + .t2e-head > .td:last-child > div > span + &:after + bottom: 50% diff --git a/app/assets/stylesheets/modules/_timetables.sass b/app/assets/stylesheets/modules/_timetables.sass new file mode 100644 index 000000000..27188060f --- /dev/null +++ b/app/assets/stylesheets/modules/_timetables.sass @@ -0,0 +1,195 @@ +//---------------// +// Time Tables // +//---------------// + +#periods + .t2e-head + > .th + height: 135px + text-align: left + border-color: $darkgrey + border-top-width: 2px + + > .strong + padding-top: 123px + transform: translateY(-1.4em) + + .t2e-head > .td, .t2e-item > .td-group > .td + height: 65px + + .t2e-head > .td + line-height: 50px + + > span + display: inline-block + vertical-align: middle + line-height: 1.4 + + .t2e-item-list > div + border-color: #fff + + .t2e-item + .th + padding: 6px 0 0 0 + border-color: $darkgrey + border-top-width: 2px + + > .monthName + padding: 0 0 6px 8px + + .monthDays + white-space: nowrap + + .day + display: inline-block + width: 40px + padding: 20px 0 6px 0 + + &.last_wday + &:not(:last-child) + border-right: 2px solid + + &:before + content: attr(data-wday) + display: block + text-align: left + color: rgba($grey, 0.5) + padding-bottom: 10px + + .dayname + display: block + text-align: center + text-transform: capitalize + padding-bottom: 2px + + .daynumber + display: block + margin: 0 auto + width: 24px + height: 24px + line-height: 24px + text-align: center + font-weight: bold + border-radius: 50% + background-color: transparent + + &.included + background-color: rgba($gold, 0.75) + + > .td-group + width: 40px + > .td + border-right: 1px solid rgba($grey, 0.5) + + &.last_wday + &:not(:last-child) > .td + border-right: 2px solid $darkgrey + + &.out_from_daytypes + background-image: linear-gradient(45deg, rgba($grey, 0.15) 0%, rgba($grey, 0.15) 49%, rgba($grey, 0.5) 50%, rgba($grey, 0.15) 51%, rgba($grey, 0.15) 99%, rgba($grey, 0.15) 100%) + background-size: 25px 25px + + > .td + &.in_periods + background-color: rgba($gold, 0.5) + border-left-color: rgba($gold, 0.5) + border-right-color: rgba($gold, 0.5) + + &.start_period + border-left-color: rgba($grey, 0.5) + &.end_period + border-right-color: rgba($grey, 0.5) + + .form-group > .month_selector + > .btn.btn-default + background-color: rgba($grey, 0.15) + color: $darkgrey + border: none + border-radius: 0 + padding: 8px 15px 7px 15px + + &:active, &.active + box-shadow: none + + > .caret + margin-left: 10px + color: $blue + + > .dropdown-menu + margin-top: 1px + border-radius: 0 0 4px 4px + max-height: 230px + overflow: auto + + .period_manager + display: block + height: auto + word-wrap: normal + white-space: nowrap + position: absolute + left: 8px + top: 50% + transform: translateY(-50%) + z-index: 5 + + > * + display: inline-block + vertical-align: middle + margin: 0 + + &.dropdown + margin-left: 5px + + .btn.dropdown-toggle + color: $blue + background-color: rgba(#fff, 0) + padding: 1px 5px + border-radius: 0 + transition: 0.2s + + &:hover, &:focus, &:active, &.active + background-color: rgba(#fff, 1) + border-color: $blue + outline: none + box-shadow: none + transition: 0.2s + + .open > .btn.dropdown-toggle + background-color: rgba(#fff, 1) + border-color: $blue + box-shadow: none + transition: 0.2s + + .dropdown-menu + margin: 0 + border-radius: 0 + box-shadow: 0 0 3px rgba($darkgrey, 0.25) + min-width: 120px + + > li > a, > li > button + padding: 5px 15px + + > li.delete-action + > a, > button + display: block + position: relative + margin-top: 11px + + &:before + content: '' + display: block + position: absolute + left: 15px + right: 15px + top: -6px + height: 1px + background-color: $grey + + .fa:first-child + margin-right: 0.5em + + .td-group.last_wday ~ .td-group.last_wday ~ .td-group.last_wday ~ .td-group.last_wday + > .td, ~ .td-group > .td + > .period_manager .dropdown-menu + left: auto + right: 0 diff --git a/app/assets/stylesheets/typography/_fonts.sass b/app/assets/stylesheets/typography/_fonts.sass index 0bb0fd2db..0cc387d74 100644 --- a/app/assets/stylesheets/typography/_fonts.sass +++ b/app/assets/stylesheets/typography/_fonts.sass @@ -35,6 +35,6 @@ //-- sBoiv --// @font-face font-family: 'sboiv' - src: url(asset-path('sBoiv/sboiv.woff?5l6pxj')) format('woff'), url(asset-path('sBoiv/sboiv.ttf?5l6pxj')) format('ttf') + src: url(asset-path('sBoiv/sboiv.woff?vhxdui')) format('woff'), url(asset-path('sBoiv/sboiv.ttf?vhxdui')) format('ttf') font-weight: normal font-style: normal diff --git a/app/assets/stylesheets/typography/_sboiv.sass b/app/assets/stylesheets/typography/_sboiv.sass index cd3af8ab7..1f89bad74 100644 --- a/app/assets/stylesheets/typography/_sboiv.sass +++ b/app/assets/stylesheets/typography/_sboiv.sass @@ -18,6 +18,8 @@ &[data-textinside] position: relative + // width: 1em + // height: 1em &:after content: attr(data-textinside) font-family: $base-font-family @@ -50,6 +52,7 @@ &.sb-5x font-size: 5em + .sb-update-vj:before content: '\e900' @@ -58,3 +61,6 @@ .sb-current-ref:before content: '\e902' + +.sb-chrono:before + content: '\e903' diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index c2414f5bb..2bdf8078a 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -15,6 +15,10 @@ class ApplicationController < ActionController::Base I18n.locale = session[:language] || I18n.default_locale end + def pundit_user + UserContext.new(current_user, referential: self.try(:current_referential)) + end + protected def user_not_authorized diff --git a/app/controllers/errors_controller.rb b/app/controllers/errors_controller.rb index c68a60804..accf119a3 100644 --- a/app/controllers/errors_controller.rb +++ b/app/controllers/errors_controller.rb @@ -1,9 +1,14 @@ class ErrorsController < ApplicationController def not_found - render :status => 404, :formats => [:html] + render template: 'errors/not_found', status: 404, formats: [:html] end def server_error - render :status => 500, :formats => [:html] + render template: 'errors/server_error', status: 500, formats: [:html] end -end
\ No newline at end of file + + def not_allowed + render template: 'errors/not_found', status: 403, formats: [:html] + end +end + diff --git a/app/controllers/journey_patterns_collections_controller.rb b/app/controllers/journey_patterns_collections_controller.rb index 51cc48c2a..ba54ddf26 100644 --- a/app/controllers/journey_patterns_collections_controller.rb +++ b/app/controllers/journey_patterns_collections_controller.rb @@ -1,6 +1,7 @@ class JourneyPatternsCollectionsController < ChouetteController respond_to :html respond_to :json + before_action :user_permissions, only: :show belongs_to :referential do belongs_to :line, :parent_class => Chouette::Line do @@ -41,6 +42,14 @@ class JourneyPatternsCollectionsController < ChouetteController @stop_points_list = @stop_points_list.sort_by {|a| a[:position] } end + def user_permissions + @perms = {}.tap do |perm| + ['journey_patterns.create', 'journey_patterns.edit', 'journey_patterns.destroy'].each do |name| + perm[name] = policy(:journey_pattern).send("#{name.split('.').last}?") + end + end.to_json + end + def update state = JSON.parse request.raw_post Chouette::JourneyPattern.state_update route, state diff --git a/app/controllers/line_footnotes_controller.rb b/app/controllers/line_footnotes_controller.rb index 7bc048731..c42aa785b 100644 --- a/app/controllers/line_footnotes_controller.rb +++ b/app/controllers/line_footnotes_controller.rb @@ -1,7 +1,6 @@ class LineFootnotesController < BreadcrumbController defaults :resource_class => Chouette::Line include PolicyChecker - before_action :check_policy, only: [:edit, :update, :destroy] respond_to :json, :only => :show belongs_to :referential @@ -27,7 +26,6 @@ class LineFootnotesController < BreadcrumbController end protected - # overrides default def check_policy authorize resource, "#{action_name}_footnote?".to_sym diff --git a/app/controllers/referential_companies_controller.rb b/app/controllers/referential_companies_controller.rb index 0966389b4..e8b104d14 100644 --- a/app/controllers/referential_companies_controller.rb +++ b/app/controllers/referential_companies_controller.rb @@ -27,7 +27,12 @@ class ReferentialCompaniesController < ChouetteController end def collection - @q = referential.workbench.companies.search(params[:q]) + scope = referential.line_referential.companies + if params[:line_id] + scope = referential.line_referential.lines.find(params[:line_id]).companies + end + + @q = scope.search(params[:q]) if sort_column && sort_direction @companies ||= @q.result(:distinct => true).order(sort_column + ' ' + sort_direction).paginate(:page => params[:page]) diff --git a/app/controllers/referentials_controller.rb b/app/controllers/referentials_controller.rb index ce875b6ba..f46cd188d 100644 --- a/app/controllers/referentials_controller.rb +++ b/app/controllers/referentials_controller.rb @@ -1,7 +1,7 @@ class ReferentialsController < BreadcrumbController defaults :resource_class => Referential include PolicyChecker - before_action :check_policy, :only => [:edit, :update, :archive, :unarchive] # overrides default + before_action :check_policy, :only => [:edit, :update, :destroy, :archive, :unarchive] # overrides default respond_to :html respond_to :json, :only => :show diff --git a/app/controllers/route_stop_points_controller.rb b/app/controllers/route_stop_points_controller.rb new file mode 100644 index 000000000..e12acb33b --- /dev/null +++ b/app/controllers/route_stop_points_controller.rb @@ -0,0 +1,18 @@ +class RouteStopPointsController < ChouetteController + defaults resource_class: Chouette::StopPoint + actions :index + respond_to :json, only: :index + + belongs_to :referential do + belongs_to :line, :parent_class => Chouette::Line do + belongs_to :route, :parent_class => Chouette::Route + end + end + + def index + respond_to do |format| + format.json { render json: referential.lines.find(params[:line_id]).routes.find(params[:route_id]).stop_points.map { |sp| { id: sp.id, name: sp.name } } } + end + end +end + diff --git a/app/controllers/routing_constraint_zones_controller.rb b/app/controllers/routing_constraint_zones_controller.rb index c39c50326..f2f74e801 100644 --- a/app/controllers/routing_constraint_zones_controller.rb +++ b/app/controllers/routing_constraint_zones_controller.rb @@ -3,6 +3,8 @@ class RoutingConstraintZonesController < ChouetteController respond_to :html, :xml, :json + before_action :remove_empty_stop_point, only: [:create, :update] + belongs_to :referential do belongs_to :line, parent_class: Chouette::Line end @@ -11,7 +13,10 @@ class RoutingConstraintZonesController < ChouetteController private def routing_constraint_zone_params - params.require(:routing_constraint_zone).permit(:name, { stop_area_ids: [] }, :line_id, :objectid, :object_version, :creator_id) + params.require(:routing_constraint_zone).permit(:name, { stop_point_ids: [] }, :line_id, :route_id, :objectid, :object_version, :creator_id) end + def remove_empty_stop_point + params.require(:routing_constraint_zone)[:stop_point_ids].delete('') + end end diff --git a/app/controllers/time_tables_controller.rb b/app/controllers/time_tables_controller.rb index bdde5d3ab..eedfef07c 100644 --- a/app/controllers/time_tables_controller.rb +++ b/app/controllers/time_tables_controller.rb @@ -18,6 +18,11 @@ class TimeTablesController < ChouetteController end end + def month + @date = params['date'] ? Date.parse(params['date']) : Date.today + @time_table = resource + end + def new @autocomplete_items = ActsAsTaggableOn::Tag.all new! do @@ -88,19 +93,25 @@ class TimeTablesController < ChouetteController protected def collection - ransack_params = params[:q] - # Hack to delete params can't be used by ransack - tag_search = ransack_params["tag_search"].split(",").collect(&:strip) if ransack_params.present? && ransack_params["tag_search"].present? - ransack_params.delete("tag_search") if ransack_params.present? + scope = select_time_tables + if params[:q] && params[:q]["tag_search"] + tags = params[:q]["tag_search"].reject {|c| c.empty?} + params[:q].delete("tag_search") + scope = select_time_tables.tagged_with(tags, :wild => true, :any => true) if tags.any? + end - selected_time_tables = tag_search ? select_time_tables.tagged_with(tag_search, :wild => true, :any => true) : select_time_tables - @q = selected_time_tables.search(ransack_params) - @time_tables ||= @q.result(:distinct => true).order(:comment).paginate(:page => params[:page]) + @q = scope.search(params[:q]) + if sort_column && sort_direction + @time_tables ||= @q.result(:distinct => true).order("#{sort_column} #{sort_direction}") + else + @time_tables ||= @q.result(:distinct => true).order(:comment) + end + @time_tables = @time_tables.paginate(page: params[:page], per_page: 10) end def select_time_tables if params[:route_id] - referential.time_tables.joins( vehicle_journeys: :route).where( "routes.id IN (#{params[:route_id]})") + referential.time_tables.joins(vehicle_journeys: :route).where( "routes.id IN (#{params[:route_id]})") else referential.time_tables end @@ -115,8 +126,34 @@ class TimeTablesController < ChouetteController end private + def sort_column + referential.time_tables.column_names.include?(params[:sort]) ? params[:sort] : 'comment' + end + def sort_direction + %w[asc desc].include?(params[:direction]) ? params[:direction] : 'asc' + end def time_table_params - params.require(:time_table).permit( :objectid, :object_version, :creator_id, :calendar_id, :version, :comment, :int_day_types, :monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday, :start_date, :end_date, { :dates_attributes => [:date, :in_out, :id, :_destroy] }, { :periods_attributes => [:period_start, :period_end, :_destroy, :id] }, :tag_list, :tag_search ) + params.require(:time_table).permit( + :objectid, + :object_version, + :creator_id, + :calendar_id, + :version, :comment, :color, + :int_day_types, + :monday, + :tuesday, + :wednesday, + :thursday, + :friday, + :saturday, + :sunday, + :start_date, + :end_date, + { :dates_attributes => [:date, :in_out, :id, :_destroy] }, + { :periods_attributes => [:period_start, :period_end, :_destroy, :id] }, + {tag_list: []}, + :tag_search + ) end end diff --git a/app/controllers/vehicle_journeys_controller.rb b/app/controllers/vehicle_journeys_controller.rb index 5199d8632..c084b592a 100644 --- a/app/controllers/vehicle_journeys_controller.rb +++ b/app/controllers/vehicle_journeys_controller.rb @@ -1,8 +1,6 @@ class VehicleJourneysController < ChouetteController defaults :resource_class => Chouette::VehicleJourney - before_action :check_policy, only: [:edit, :update, :destroy] before_action :user_permissions, only: :index - before_action :ransack_params, only: :index respond_to :json, :only => :index respond_to :js, :only => [:select_journey_pattern, :edit, :new, :index] @@ -61,8 +59,7 @@ class VehicleJourneysController < ChouetteController } end - @jp_origin = Chouette::JourneyPattern.find_by(objectid: params[:jp]) - + @jp_origin = Chouette::JourneyPattern.find_by(objectid: params[:jp]) index! do if collection.out_of_bounds? @@ -80,14 +77,39 @@ class VehicleJourneysController < ChouetteController protected def collection + scope = route.vehicle_journeys.joins(:journey_pattern).joins('LEFT JOIN "vehicle_journey_at_stops" ON "vehicle_journey_at_stops"."vehicle_journey_id" = "vehicle_journeys"."id"') + + @q = scope.search filtered_ransack_params + grouping = ransack_periode_filter + @q.build_grouping(grouping) if grouping + @ppage = 20 - @q = route.sorted_vehicle_journeys('vehicle_journeys').search params[:q] - @vehicle_journeys = @q.result.paginate(:page => params[:page], :per_page => @ppage) + @vehicle_journeys = @q.result(distinct: true).paginate(:page => params[:page], :per_page => @ppage) @footnotes = route.line.footnotes.to_json @matrix = resource_class.matrix(@vehicle_journeys) @vehicle_journeys end + def ransack_periode_filter + if params[:q] && params[:q][:vehicle_journey_at_stops_departure_time_gteq] + params[:q] = params[:q].reject{|k| params[:q][k] == 'undefined'} + between = [:departure_time_gteq, :departure_time_lteq].map do |filter| + "2000-01-01 #{params[:q]["vehicle_journey_at_stops_#{filter}"]}:00 UTC" + end + { + :m => 'or', + :vehicle_journey_at_stops_departure_time_between => between.join(' to '), + :vehicle_journey_at_stops_id_null => params[:q][:vehicle_journey_without_departure_time] + } + end + end + + def filtered_ransack_params + if params[:q] + params[:q].except(:vehicle_journey_at_stops_departure_time_gteq, :vehicle_journey_at_stops_departure_time_lteq) + end + end + def adapted_params params.tap do |adapted_params| adapted_params.merge!( :route => parent) @@ -107,30 +129,15 @@ class VehicleJourneysController < ChouetteController @matrix = resource_class.matrix(@vehicle_journeys) end - def check_policy - authorize resource - end - def user_permissions @perms = {}.tap do |perm| ['vehicle_journeys.create', 'vehicle_journeys.edit', 'vehicle_journeys.destroy'].each do |name| - perm[name] = current_user.permissions.include?(name) + perm[name] = policy(:vehicle_journey).send("#{name.split('.').last}?") end - end - @perms = @perms.to_json + end.to_json end private - def ransack_params - if params[:q] - params[:q] = params[:q].reject{|k| params[:q][k] == 'undefined'} - [:departure_time_gteq, :departure_time_lteq].each do |filter| - time = params[:q]["vehicle_journey_at_stops_#{filter}"] - params[:q]["vehicle_journey_at_stops_#{filter}"] = "2000-01-01 #{time}:00 UTC" - end - end - end - def vehicle_journey_params params.require(:vehicle_journey).permit( { footnote_ids: [] } , :journey_pattern_id, :number, :published_journey_name, :published_journey_identifier, :comment, :transport_mode, diff --git a/app/controllers/workbenches_controller.rb b/app/controllers/workbenches_controller.rb index 1424fe03c..d621a28d7 100644 --- a/app/controllers/workbenches_controller.rb +++ b/app/controllers/workbenches_controller.rb @@ -37,10 +37,11 @@ class WorkbenchesController < BreadcrumbController end def sort_result collection - col = (Workbench.find(params[:id]).referentials.column_names + %w{lines}).include?(params[:sort]) ? params[:sort] : 'name' + col = (Workbench.find(params[:id]).referentials.column_names + %w{lines validity_period}).include?(params[:sort]) ? params[:sort] : 'name' dir = %w[asc desc].include?(params[:direction]) ? params[:direction] : 'asc' - if col == "lines" - collection.joins(:metadatas).group("referentials.id").order("sum(array_length(referential_metadata.line_ids,1)) #{dir}") + + if ['lines', 'validity_period'].include?(col) + collection.send("order_by_#{col}", dir) else collection.order("#{col} #{dir}") end diff --git a/app/helpers/access_links_helper.rb b/app/helpers/access_links_helper.rb new file mode 100644 index 000000000..8df452f65 --- /dev/null +++ b/app/helpers/access_links_helper.rb @@ -0,0 +1,7 @@ +module AccessLinksHelper + def access_link_type_label_pairs + Chouette::AccessLink + .access_link_types + .zip_map { |type| t("connection_link_types.label.#{type}") } + end +end diff --git a/app/helpers/access_points_helper.rb b/app/helpers/access_points_helper.rb index c5e9f672e..c954e6cad 100644 --- a/app/helpers/access_points_helper.rb +++ b/app/helpers/access_points_helper.rb @@ -1,28 +1,14 @@ module AccessPointsHelper - def access_links_pairs(access_links) - hpairs = Hash.new - pairs = Array.new - access_links.each do |link| - key = pair_key(link) - pair = nil - if (hpairs.has_key? key) - pair = hpairs[key] - else - pair = AccessLinkPair.new - pairs << pair - hpairs[key] = pair - end - if (link.link_orientation_type == "access_point_to_stop_area") - pair.from_access_point = link - else - pair.to_access_point = link - end - end - pairs + + + def access_point_type_label_pairs + Chouette::AccessPoint + .access_point_types + .zip_map { |access_point_type| t("access_types.label.#{access_point_type}") } end - + def pair_key(access_link) "#{access_link.access_point.id}-#{access_link.stop_area.id}" end - + end diff --git a/app/helpers/common_helpers.rb b/app/helpers/common_helpers.rb new file mode 100644 index 000000000..29cabddac --- /dev/null +++ b/app/helpers/common_helpers.rb @@ -0,0 +1,26 @@ + +module CommonHelpers + # TODO: Needs refactoring, but does not seem to be under test + # so let us refactor this **after** test coverage. + def access_links_pairs(access_links) + hpairs = Hash.new + pairs = Array.new + access_links.each do |link| + key = pair_key(link) + pair = nil + if (hpairs.has_key? key) + pair = hpairs[key] + else + pair = AccessLinkPair.new + pairs << pair + hpairs[key] = pair + end + if (link.link_orientation_type == "access_point_to_stop_area") + pair.from_access_point = link + else + pair.to_access_point = link + end + end + pairs + end +end diff --git a/app/helpers/connection_links_helper.rb b/app/helpers/connection_links_helper.rb new file mode 100644 index 000000000..d9c201028 --- /dev/null +++ b/app/helpers/connection_links_helper.rb @@ -0,0 +1,8 @@ +module ConnectionLinksHelper + + def connection_link_type_label_pairs + Chouette::ConnectionLink + .connection_link_types + .zip_map { |type| t("connection_link_types.label.#{type}") } + end +end diff --git a/app/helpers/core_helper.rb b/app/helpers/core_helper.rb new file mode 100644 index 000000000..af2f6664c --- /dev/null +++ b/app/helpers/core_helper.rb @@ -0,0 +1,7 @@ +module CoreHelper + module ::Enumerable + def zip_map + map { |ele| [ele, yield(ele)] } + end + end +end diff --git a/app/helpers/networks_helper.rb b/app/helpers/networks_helper.rb new file mode 100644 index 000000000..b881ce078 --- /dev/null +++ b/app/helpers/networks_helper.rb @@ -0,0 +1,7 @@ +module NetworksHelper + + def source_type_name_label_pairs + Chouette::Network.source_type_names + .zip_map { |source_type_name| t("source_types.label.#{source_type_name}") } + end +end diff --git a/app/helpers/newapplication_helper.rb b/app/helpers/newapplication_helper.rb index c9da544d1..42fca489f 100644 --- a/app/helpers/newapplication_helper.rb +++ b/app/helpers/newapplication_helper.rb @@ -16,7 +16,7 @@ module NewapplicationHelper end columns.map do |k, v| - if ["ID Codif", "Oid", "OiD", "ID Reflex", "Arrêt de départ", "Arrêt d'arrivée"].include? k + if ["ID Codif", "Oid", "OiD", "ID Reflex", "Arrêt de départ", "Arrêt d'arrivée", "Période de validité englobante"].include? k hcont << content_tag(:th, k) else hcont << content_tag(:th, sortable_columns(collection, k)) @@ -30,6 +30,7 @@ module NewapplicationHelper body = content_tag :tbody do collection.collect do |item| + content_tag :tr do bcont = [] @@ -47,17 +48,17 @@ module NewapplicationHelper else item.try(attribute) end - if attribute == 'name' + if attribute == 'name' or attribute == 'comment' lnk = [] - unless item.class.to_s == 'Calendar' or item.class.to_s == 'Referential' + unless item.class == Calendar or item.class == Referential if current_referential lnk << current_referential lnk << item.line if item.respond_to? :line - lnk << item if item.class.to_s == 'Chouette::RoutingConstraintZone' + lnk << item.route.line if item.class == Chouette::RoutingConstraintZone lnk << item if item.respond_to? :line_referential lnk << item.stop_area if item.respond_to? :stop_area - lnk << item if item.respond_to? :stop_points + lnk << item if item.respond_to? :stop_points or item.class.to_s == 'Chouette::TimeTable' elsif item.respond_to? :referential lnk << item.referential end @@ -101,14 +102,14 @@ module NewapplicationHelper polymorph_url << action end - unless item.class.to_s == 'Calendar' or item.class.to_s == 'Referential' + unless item.class == Calendar or item.class == Referential if current_referential polymorph_url << current_referential polymorph_url << item.line if item.respond_to? :line - polymorph_url << item if item.class.to_s == 'Chouette::RoutingConstraintZone' + polymorph_url << item.route.line if item.class == Chouette::RoutingConstraintZone polymorph_url << item if item.respond_to? :line_referential polymorph_url << item.stop_area if item.respond_to? :stop_area - polymorph_url << item if item.respond_to? :stop_points + polymorph_url << item if item.respond_to? :stop_points or item.class.to_s == 'Chouette::TimeTable' elsif item.respond_to? :referential polymorph_url << item.referential end diff --git a/app/helpers/rule_parameter_sets_helper.rb b/app/helpers/rule_parameter_sets_helper.rb index 538a5cddd..bb210d9cd 100644 --- a/app/helpers/rule_parameter_sets_helper.rb +++ b/app/helpers/rule_parameter_sets_helper.rb @@ -18,6 +18,11 @@ module RuleParameterSetsHelper t "false" end + def transport_mode_label_pairs + Chouette::TransportMode + .all + .zip_map { |mode| t("transport_modes.label.#{mode}") } + end end diff --git a/app/helpers/stop_area_copies_helper.rb b/app/helpers/stop_area_copies_helper.rb new file mode 100644 index 000000000..023a9d750 --- /dev/null +++ b/app/helpers/stop_area_copies_helper.rb @@ -0,0 +1,8 @@ +module StopAreaCopiesHelper + + def label_stop_area_types(*stop_area_types) + stop_area_types + .flatten + .zip_map { |stop_area_type| t("area_types.label.#{stop_area_type}") } + end +end diff --git a/app/helpers/stop_areas_helper.rb b/app/helpers/stop_areas_helper.rb index 2188a272d..3e04fac7d 100644 --- a/app/helpers/stop_areas_helper.rb +++ b/app/helpers/stop_areas_helper.rb @@ -33,27 +33,6 @@ module StopAreasHelper @stop_area.stop_area_type == 'stop_place' || @stop_area.stop_area_type == 'commercial_stop_point' end - def access_links_pairs(access_links) - hpairs = Hash.new - pairs = Array.new - access_links.each do |link| - key = pair_key(link) - pair = nil - if (hpairs.has_key? key) - pair = hpairs[key] - else - pair = AccessLinkPair.new - pairs << pair - hpairs[key] = pair - end - if (link.link_orientation_type == "access_point_to_stop_area") - pair.from_access_point = link - else - pair.to_access_point = link - end - end - pairs - end def pair_key(access_link) "#{access_link.access_point.id}-#{access_link.stop_area.id}" diff --git a/app/helpers/time_tables_helper.rb b/app/helpers/time_tables_helper.rb index 9fdb791b1..b380a2b0a 100644 --- a/app/helpers/time_tables_helper.rb +++ b/app/helpers/time_tables_helper.rb @@ -1,3 +1,178 @@ +require 'date' + module TimeTablesHelper -end + def month_periode_enum(years) + start_date = Date.today - years.years + end_date = Date.today + years.years + (start_date..end_date).map(&:beginning_of_month).uniq.map(&:to_s) + end + + def new_alt_calendar(options = {}, &block) + raise(ArgumentError, "No year given") unless options.has_key?(:year) + raise(ArgumentError, "No month given") unless options.has_key?(:month) + + block ||= Proc.new {|d| nil} + + month_names = (!defined?(I18n) || I18n.t("date.month_names").include?("missing")) ? Date::MONTHNAMES.dup : I18n.t("date.month_names") + + defaults = { + :table_id => "calendar-#{options[:year]}-#{"%02d" % options[:month]}", + :table_class => 'calendar', + :month_name_class => 'monthName', + :other_month_class => 'outsideMonth', + :day_name_class => 'dayName', + :day_class => 'day', + :abbrev => false, + :first_day_of_week => 0, + :accessible => false, + :show_today => true, + :previous_month_text => nil, + :next_month_text => nil, + :month_header => true, + :calendar_title => month_names[options[:month]], + :summary => "Calendrier pour #{month_names[options[:month]]} #{options[:year]}" + } + options = defaults.merge options + + first = Date.civil(options[:year], options[:month], 1) + last = Date.civil(options[:year], options[:month], -1) + + first_weekday = first_day_of_week(options[:first_day_of_week]) + last_weekday = last_day_of_week(options[:first_day_of_week]) + + day_names = (!defined?(I18n) || I18n.t("date.day_names").include?("missing")) ? Date::DAYNAMES : I18n.t("date.day_names") + abbr_day_names = (!defined?(I18n) || I18n.t("date.abbr_day_names").include?("missing")) ? Date::ABBR_DAYNAMES : I18n.t("date.abbr_day_names") + week_days = (0..6).to_a + first_weekday.times do + week_days.push(week_days.shift) + end + + # TODO Use some kind of builder instead of straight HTML + cal = %(<table id="#{options[:table_id]}" class="#{options[:table_class]}" border="0" cellspacing="0" cellpadding="0" summary="#{options[:summary]}">) + cal << %(<thead>) + + if (options[:month_header]) + cal << %(<tr>) + cal << %(<th class='weekNumber' scope="col"></th>) + if options[:previous_month_text] or options[:next_month_text] + cal << %(<th colspan="2">#{options[:previous_month_text]}</th>) + colspan=3 + else + colspan=7 + end + cal << %(<th colspan="#{colspan}" class="#{options[:month_name_class]}">#{options[:calendar_title]}</th>) + cal << %(<th colspan="2">#{options[:next_month_text]}</th>) if options[:next_month_text] + cal << %(</tr>) + end + + cal << %(<tr class="#{options[:day_name_class]}">) + cal << %(<th class='weekNumber' scope="col"></th>) + + week_days.each do |wday| + cal << %(<th id="#{th_id(Date::DAYNAMES[wday], options[:table_id])}" scope="col">) + cal << (options[:abbrev] ? %(<abbr title="#{day_names[wday]}">#{t("calendars.days.#{Date::DAYNAMES[wday].downcase}")}</abbr>) : t("calendars.days.#{Date::DAYNAMES[wday].downcase}")) + cal << %(</th>) + end + + cal << "</tr></thead><tbody><tr>" + + # previous month + beginning_of_week(first, first_weekday).upto(first - 1) do |d| + cal << "<td class='weekNumber'>S#{d.strftime("%W").to_s}</td>" if d.wday == first_weekday + cal << generate_other_month_cell(d, options) + end unless first.wday == first_weekday + + first.upto(last) do |cur| + cell_text, cell_attrs = block.call(cur) + cell_text ||= cur.mday + cell_attrs ||= {} + cell_attrs[:headers] = th_id(cur, options[:table_id]) + cell_attrs[:class] ||= options[:day_class] + cell_attrs[:class] += " weekend" if [0, 6].include?(cur.wday) + today = (Time.respond_to?(:zone) && !(zone = Time.zone).nil? ? zone.now.to_date : Date.today) + cell_attrs[:class] += " today" if (cur == today) and options[:show_today] + + cal << "<td class='weekNumber'>S#{cur.strftime("%W").to_s}</td>" if cur.wday == first_weekday + + cal << generate_cell(cell_text, cell_attrs) + cal << "</tr><tr>" if cur.wday == last_weekday + end + + # next month + (last + 1).upto(beginning_of_week(last + 7, first_weekday) - 1) do |d| + cal << generate_other_month_cell(d, options) + end unless last.wday == last_weekday + + cal << "</tr></tbody></table>" + cal.respond_to?(:html_safe) ? cal.html_safe : cal + end + + private + + def first_day_of_week(day) + day + end + + def last_day_of_week(day) + if day > 0 + day - 1 + else + 6 + end + end + + def days_between(first, second) + if first > second + second + (7 - first) + else + second - first + end + end + + def beginning_of_week(date, start = 1) + days_to_beg = days_between(start, date.wday) + date - days_to_beg + end + + def generate_cell(cell_text, cell_attrs) + cell_attrs = cell_attrs.map {|k, v| %(#{k}="#{v}") }.join(" ") + "<td #{cell_attrs}>#{cell_text}</td>" + end + + def generate_other_month_cell(date, options) + cell_attrs = {} + cell_attrs[:headers] = th_id(date, options[:table_id]) + cell_attrs[:class] = options[:other_month_class] + cell_attrs[:class] += " weekend" if weekend?(date) + cell_attrs[:title] ||= date.strftime("%W").to_i if options[:first_day_of_week] == 1 + + cell_text = date.day + if options[:accessible] + cell_text += %(<span class="hidden"> #{month_names[date.month]}</span>) + end + + generate_cell(date.day, cell_attrs) + end + + # Calculates id for th element. + # derived from calendar_id and dow. + # + # Params: + # `day` can be either Date or DOW('Sunday', 'Monday') + def th_id(day, calendar_id) + return th_id(Date::DAYNAMES[day.wday], calendar_id) if day.is_a?(Date) + "#{calendar_id}-#{day[0..2].downcase}" + end + + def weekend?(date) + [0, 6].include?(date.wday) + end + + class Engine < Rails::Engine # :nodoc: + ActiveSupport.on_load(:action_view) do + include CalendarHelper + end + end if defined? Rails::Engine + +end diff --git a/app/helpers/vehicle_journeys_helper.rb b/app/helpers/vehicle_journeys_helper.rb index e70f2954b..6877abd11 100644 --- a/app/helpers/vehicle_journeys_helper.rb +++ b/app/helpers/vehicle_journeys_helper.rb @@ -41,6 +41,12 @@ module VehicleJourneysHelper end end + def route_journey_pattern_label_pairs route + route + .journey_patterns + .zip_map { |jp| journey_name(jp) } + end + def edit_vehicle_title( vehicle) return t('vehicle_journeys.edit.title_stopless', :name => vehicle_name( vehicle)) if vehicle.vehicle_journey_at_stops.empty? first_vjas = vehicle.vehicle_journey_at_stops.first diff --git a/app/inputs/tags_input.rb b/app/inputs/tags_input.rb index 4fbf0465f..1dc6129ee 100644 --- a/app/inputs/tags_input.rb +++ b/app/inputs/tags_input.rb @@ -1,19 +1,16 @@ -class TagsInput < Formtastic::Inputs::StringInput - - def to_html - input_wrapping do - label_html << - '<span id="tagsContainer"></span>'.html_safe << - builder.text_field(method, input_html_options) - end +class TagsInput < SimpleForm::Inputs::CollectionInput + enable :placeholder + + def input(wrapper_options = {}) + @collection ||= @builder.object.send(attribute_name) + label_method, value_method = detect_collection_methods + + merged_input_options = merge_wrapper_options(input_html_options, wrapper_options) + merged_input_options.reverse_merge!(multiple: true) + + @builder.collection_select( + attribute_name, collection, value_method, label_method, + input_options, merged_input_options + ) end - - def input_html_options - super.merge({ - :required => nil, - :autofocus => nil, - :class => 'tm-input', - }) - end - end diff --git a/app/models/chouette/line.rb b/app/models/chouette/line.rb index 27a37a7f4..f44375e7d 100644 --- a/app/models/chouette/line.rb +++ b/app/models/chouette/line.rb @@ -14,20 +14,20 @@ class Chouette::Line < Chouette::ActiveRecord belongs_to :company belongs_to :network + belongs_to :line_referential has_array_of :secondary_companies, class_name: 'Chouette::Company' has_many :routes, :dependent => :destroy has_many :journey_patterns, :through => :routes has_many :vehicle_journeys, :through => :journey_patterns + has_many :routing_constraint_zones, through: :routes has_and_belongs_to_many :group_of_lines, :class_name => 'Chouette::GroupOfLine', :order => 'group_of_lines.name' has_many :footnotes, :inverse_of => :line, :validate => :true, :dependent => :destroy accepts_nested_attributes_for :footnotes, :reject_if => :all_blank, :allow_destroy => true - has_many :routing_constraint_zones - attr_reader :group_of_line_tokens # validates_presence_of :network @@ -76,4 +76,8 @@ class Chouette::Line < Chouette::ActiveRecord [name, company.try(:name)].compact.join(' - ') end + def companies + line_referential.companies.where(id: ([company_id] + Array(secondary_company_ids)).compact) + end + end diff --git a/app/models/chouette/route.rb b/app/models/chouette/route.rb index 9de7d7470..429189ff5 100644 --- a/app/models/chouette/route.rb +++ b/app/models/chouette/route.rb @@ -5,7 +5,7 @@ class Chouette::Route < Chouette::TridentActiveRecord extend ActiveModel::Naming enumerize :direction, in: %i(straight_forward backward clockwise counter_clockwise north north_west west south_west south south_east east north_east) - enumerize :wayback, in: %i(straight_forward backward), default: :backward + enumerize :wayback, in: %i(straight_forward backward), default: :straight_forward # FIXME http://jira.codehaus.org/browse/JRUBY-6358 self.primary_key = "id" @@ -16,6 +16,7 @@ class Chouette::Route < Chouette::TridentActiveRecord belongs_to :line + has_many :routing_constraint_zones has_many :journey_patterns, :dependent => :destroy has_many :vehicle_journeys, :dependent => :destroy do def timeless diff --git a/app/models/chouette/routing_constraint_zone.rb b/app/models/chouette/routing_constraint_zone.rb index 681069416..6a8847e4d 100644 --- a/app/models/chouette/routing_constraint_zone.rb +++ b/app/models/chouette/routing_constraint_zone.rb @@ -1,9 +1,24 @@ class Chouette::RoutingConstraintZone < Chouette::TridentActiveRecord - belongs_to :line - has_array_of :stop_areas, class_name: 'Chouette::StopArea' + belongs_to :route + has_array_of :stop_points, class_name: 'Chouette::StopPoint' - validates_presence_of :name, :stop_area_ids, :line_id - validates :stop_areas, length: { minimum: 2 } + validates_presence_of :name, :stop_point_ids, :route_id + validates :stop_point_ids, length: { minimum: 2, too_short: I18n.t('activerecord.errors.models.routing_constraint_zone.attributes.stop_points.not_enough_stop_points') } + validate :stop_points_belong_to_route, :not_all_stop_points_selected - self.primary_key = 'id' + def stop_points_belong_to_route + errors.add(:stop_point_ids, I18n.t('activerecord.errors.models.routing_constraint_zone.attributes.stop_points.stop_points_not_from_route')) unless stop_points.all? { |sp| route.stop_points.include? sp } + end + + def not_all_stop_points_selected + errors.add(:stop_point_ids, I18n.t('activerecord.errors.models.routing_constraint_zone.attributes.stop_points.all_stop_points_selected')) if stop_points.length == route.stop_points.length + end + + def stop_points_count + stop_points.count + end + + def route_name + route.name + end end diff --git a/app/models/chouette/stop_point.rb b/app/models/chouette/stop_point.rb index b77189fc1..e0f947487 100644 --- a/app/models/chouette/stop_point.rb +++ b/app/models/chouette/stop_point.rb @@ -2,7 +2,7 @@ module Chouette class StopPoint < TridentActiveRecord include ForBoardingEnumerations include ForAlightingEnumerations - + # FIXME http://jira.codehaus.org/browse/JRUBY-6358 self.primary_key = "id" @@ -16,7 +16,9 @@ module Chouette validates_presence_of :stop_area validate :stop_area_id_validation - scope :default_order, order("position") + scope :default_order, -> { order("position") } + + delegate :name, to: :stop_area before_destroy :remove_dependent_journey_pattern_stop_points def remove_dependent_journey_pattern_stop_points @@ -25,7 +27,7 @@ module Chouette jp.stop_point_ids = jp.stop_point_ids - [id] end end - end + end def stop_area_id_validation if stop_area_id.nil? diff --git a/app/models/chouette/time_table.rb b/app/models/chouette/time_table.rb index 7afdc4529..086e6fa77 100644 --- a/app/models/chouette/time_table.rb +++ b/app/models/chouette/time_table.rb @@ -43,6 +43,19 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord [Chouette::TimeTable.maximum(:end_date)].compact.max end + def month_inspect(date) + (date.beginning_of_month..date.end_of_month).map do |d| + { + day: I18n.l(d, format: '%A'), + wday: d.wday, + wnumber: d.strftime("%W").to_s, + mday: d.mday, + include_date: include_in_dates?(d), + excluded_date: excluded_date?(d) + } + end + end + def save_shortcuts shortcuts_update self.update_column(:start_date, start_date) @@ -454,4 +467,3 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord tt end end - diff --git a/app/models/chouette/vehicle_journey.rb b/app/models/chouette/vehicle_journey.rb index 0aab9d0ed..4d7d596d8 100644 --- a/app/models/chouette/vehicle_journey.rb +++ b/app/models/chouette/vehicle_journey.rb @@ -49,27 +49,61 @@ module Chouette def vehicle_journey_at_stops_matrix at_stops = self.vehicle_journey_at_stops.to_a.dup - filling = route.stop_points.map(&:id) - at_stops.map(&:stop_point_id) - filling.each do |id| - at_stops.insert(route.stop_points.map(&:id).index(id), Chouette::VehicleJourneyAtStop.new()) + (route.stop_points.map(&:id) - at_stops.map(&:stop_point_id)).each do |id| + # Set stop_point id for fake vjas with no departure time yep. + params = {} + params[:stop_point_id] = id if journey_pattern.stop_points.map(&:id).include?(id) + at_stops.insert(route.stop_points.map(&:id).index(id), Chouette::VehicleJourneyAtStop.new(params)) end at_stops end + def create_or_find_vjas_from_state vjas + return vehicle_journey_at_stops.find(vjas['id']) if vjas['id'] + stop_point = Chouette::StopPoint.find_by(objectid: vjas['stop_point_objectid']) + stop = vehicle_journey_at_stops.create(stop_point: stop_point) + vjas['id'] = stop.id + vjas['new_record'] = true + stop + end + def update_vjas_from_state state state.each do |vjas| next if vjas["dummy"] - stop = vehicle_journey_at_stops.find(vjas['id']) if vjas['id'] - if stop - params = {}.tap do |el| - ['arrival_time', 'departure_time'].each do |field| - time = "#{vjas[field]['hour']}:#{vjas[field]['minute']}" - el[field.to_sym] = Time.parse("2000-01-01 #{time}:00 UTC") - end + params = {}.tap do |el| + ['arrival_time', 'departure_time'].each do |field| + time = "#{vjas[field]['hour']}:#{vjas[field]['minute']}" + el[field.to_sym] = Time.parse("2000-01-01 #{time}:00 UTC") + end + end + stop = create_or_find_vjas_from_state(vjas) + stop.update_attributes(params) + vjas.delete('errors') + vjas['errors'] = stop.errors if stop.errors.any? + end + end + + def state_update_vjas? vehicle_journey_at_stops + departure_times = vehicle_journey_at_stops.map do |vjas| + "#{vjas['departure_time']['hour']}:#{vjas['departure_time']['minute']}" + end + times = departure_times.uniq + (times.count == 1 && times[0] == '00:00') ? false : true + end + + def update_has_and_belongs_to_many_from_state item + ['time_tables', 'footnotes'].each do |assos| + saved = self.send(assos).map(&:id) + + (saved - item[assos].map{|t| t['id']}).each do |id| + self.send(assos).delete(self.send(assos).find(id)) + end + + item[assos].each do |t| + klass = "Chouette::#{assos.classify}".constantize + unless saved.include?(t['id']) + self.send(assos) << klass.find(t['id']) end - stop.update_attributes(params) - vjas.delete('errors') - vjas['errors'] = stop.errors if stop.errors.any? end end end @@ -81,17 +115,30 @@ module Chouette vj = find_by(objectid: item['objectid']) || state_create_instance(route, item) next if item['deletable'] && vj.persisted? && vj.destroy - vj.update_vjas_from_state(item['vehicle_journey_at_stops']) + if vj.state_update_vjas?(item['vehicle_journey_at_stops']) + vj.update_vjas_from_state(item['vehicle_journey_at_stops']) + end + vj.update_attributes(state_permited_attributes(item)) + vj.update_has_and_belongs_to_many_from_state(item) item['errors'] = vj.errors if vj.errors.any? end + + # Delete ids of new object from state if we had to rollback if state.any? {|item| item['errors']} - state.map {|item| item.delete('objectid') if item['new_record']} + state.map do |item| + item.delete('objectid') if item['new_record'] + item['vehicle_journey_at_stops'].map {|vjas| vjas.delete('id') if vjas['new_record'] } + end raise ::ActiveRecord::Rollback end end - state.map {|item| item.delete('new_record')} + # Remove new_record flag && deleted item from state if transaction has been saved + state.map do |item| + item.delete('new_record') + item['vehicle_journey_at_stops'].map {|vjas| vjas.delete('new_record') } + end state.delete_if {|item| item['deletable']} end diff --git a/app/models/import.rb b/app/models/import.rb index c407daa78..d0736ab0b 100644 --- a/app/models/import.rb +++ b/app/models/import.rb @@ -4,11 +4,12 @@ class Import < ActiveRecord::Base belongs_to :referential extend Enumerize - enumerize :status, in: %i(new pending successful failed canceled) + enumerize :status, in: %i(new pending successful failed running aborted canceled) validates :file, presence: true before_create do self.token_download = SecureRandom.urlsafe_base64 + self.status = Import.status.new end end diff --git a/app/models/netex_import.rb b/app/models/netex_import.rb index 27f5846af..0da008d2e 100644 --- a/app/models/netex_import.rb +++ b/app/models/netex_import.rb @@ -1,3 +1,13 @@ +require 'net/http' class NetexImport < Import + after_create :launch_java_import + def launch_java_import + logger.warn "Call iev get #{Rails.configuration.iev_url}/boiv_iev/referentials/importer/new?id=#{id}" + begin + Net::HTTP.get(Rails.configuration.iev_url, "/boiv_iev/referentials/importer/new?id=#{id}") + rescue + logger.error("IEV server error") + end + end end diff --git a/app/models/referential.rb b/app/models/referential.rb index b6a83c6c3..50db32637 100644 --- a/app/models/referential.rb +++ b/app/models/referential.rb @@ -45,6 +45,8 @@ class Referential < ActiveRecord::Base scope :ready, -> { where(ready: true) } scope :in_periode, ->(periode) { where(id: referential_ids_in_periode(periode)) } scope :include_metadatas_lines, ->(line_ids) { where('referential_metadata.line_ids && ARRAY[?]::bigint[]', line_ids) } + scope :order_by_validity_period, ->(dir) { joins(:metadatas).order("unnest(periodes) #{dir}") } + scope :order_by_lines, ->(dir) { joins(:metadatas).group("referentials.id").order("sum(array_length(referential_metadata.line_ids,1)) #{dir}") } def lines if metadatas.blank? @@ -232,11 +234,19 @@ class Referential < ActiveRecord::Base def self.referential_ids_in_periode(range) subquery = "SELECT DISTINCT(public.referential_metadata.referential_id) FROM public.referential_metadata, LATERAL unnest(periodes) period " - subquery << "WHERE period && '#{ActiveRecord::ConnectionAdapters::PostgreSQLColumn.range_to_string(range)}'" + subquery << "WHERE period && '#{range_to_string(range)}'" query = "SELECT * FROM public.referentials WHERE referentials.id IN (#{subquery})" self.connection.select_values(query).map(&:to_i) end + # Copied from Rails 4.1 activerecord/lib/active_record/connection_adapters/postgresql/cast.rb + # TODO: Relace with the appropriate Rais 4.2 / 5.x helper if one is found. + def self.range_to_string(object) + from = object.begin.respond_to?(:infinite?) && object.begin.infinite? ? '' : object.begin + to = object.end.respond_to?(:infinite?) && object.end.infinite? ? '' : object.end + "[#{from},#{to}#{object.exclude_end? ? ')' : ']'}" + end + def overlapped_referential_ids return [] unless metadatas.present? @@ -248,7 +258,7 @@ class Referential < ActiveRecord::Base not_myself = "and referential_id != #{id}" if persisted? periods_query = periodes.map do |periode| - "period && '#{ActiveRecord::ConnectionAdapters::PostgreSQLColumn.range_to_string(periode)}'" + "period && '[#{periode.begin},#{periode.end})'" end.join(" OR ") query = "select distinct(public.referential_metadata.referential_id) FROM public.referential_metadata, unnest(line_ids) line, LATERAL unnest(periodes) period diff --git a/app/models/referential_metadata.rb b/app/models/referential_metadata.rb index 62faf5541..3ec8398e0 100644 --- a/app/models/referential_metadata.rb +++ b/app/models/referential_metadata.rb @@ -12,6 +12,25 @@ class ReferentialMetadata < ActiveRecord::Base scope :include_lines, -> (line_ids) { where('line_ids && ARRAY[?]::bigint[]', line_ids) } scope :include_dateranges, -> (dateranges) { where('periodes && ARRAY[?]', dateranges) } +# Transform Wed, 22 Feb 2017...Fri, 24 Feb 2017 into Wed, 22 Feb 2017..Thu, 23 Feb 2017 + def periodes + attributes["periodes"].tap do | periods | + return periods unless periods + return adapted_periods(periods) + end + end + + def adapted_periods(periods) + periods.map do | period | + if period.try(:exclude_end?) + period.begin .. (period.end - 1) + else + period + end + end + end + private :adapted_periods + class Period include ActiveAttr::Model include ActiveAttr::MultiParameterAttributes diff --git a/app/models/user.rb b/app/models/user.rb index b921cdef8..8e73aa1d5 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -21,7 +21,6 @@ class User < ActiveRecord::Base validates :organisation, :presence => true validates :email, :presence => true, :uniqueness => true validates :name, :presence => true - validate :permissions_unique_and_nonempty before_validation(:on => :create) do self.password ||= Devise.friendly_token.first(6) @@ -32,16 +31,14 @@ class User < ActiveRecord::Base @@edit_offer_permissions = ['routes.create', 'routes.edit', 'routes.destroy', 'journey_patterns.create', 'journey_patterns.edit', 'journey_patterns.destroy', 'vehicle_journeys.create', 'vehicle_journeys.edit', 'vehicle_journeys.destroy', 'time_tables.create', 'time_tables.edit', 'time_tables.destroy', 'footnotes.edit', 'footnotes.create', 'footnotes.destroy', 'routing_constraint_zones.create', 'routing_constraint_zones.edit', - 'routing_constraint_zones.destroy'] + 'routing_constraint_zones.destroy', 'referentials.create', 'referentials.edit', 'referentials.destroy'] def cas_extra_attributes=(extra_attributes) extra = extra_attributes.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo} self.name = extra[:full_name] self.email = extra[:email] self.organisation = Organisation.sync_update extra[:organisation_code], extra[:organisation_name], extra[:functional_scope] - if extra[:permissions] && extra[:permissions].include?('boiv:edit-offer') - self.permissions = @@edit_offer_permissions - end + self.permissions = extra[:permissions].include?('boiv:edit-offer') ? @@edit_offer_permissions : [] end def self.portail_api_request @@ -69,10 +66,7 @@ class User < ActiveRecord::Base user.locked_at = el['locked_at'] user.organisation = Organisation.sync_update el['organization_code'], el['organization_name'], el['functional_scope'] user.synced_at = Time.now - - if el['permissions'] && el['permissions'].include?('boiv:edit-offer') - user.permissions = @@edit_offer_permissions - end + user.permissions = el['permissions'].include?('boiv:edit-offer') ? @@edit_offer_permissions : [] user.save puts "✓ user #{user.username} has been updated" unless Rails.env.test? end @@ -91,10 +85,4 @@ class User < ActiveRecord::Base end end - def permissions_unique_and_nonempty - if permissions && permissions.any? - errors.add(:permissions, I18n.t('activerecord.errors.models.calendar.attributes.permissions.must_be_unique')) if permissions.uniq.length != permissions.length - errors.add(:permissions, I18n.t('activerecord.errors.models.calendar.attributes.permissions.must_be_nonempty')) if permissions.include? '' - end - end end diff --git a/app/models/user_context.rb b/app/models/user_context.rb new file mode 100644 index 000000000..e0a856e4b --- /dev/null +++ b/app/models/user_context.rb @@ -0,0 +1,8 @@ +class UserContext + attr_reader :user, :context + + def initialize(user, context = {}) + @user = user + @context = context + end +end diff --git a/app/policies/acces_point_policy.rb b/app/policies/acces_point_policy.rb index 4f604693c..904b7a242 100644 --- a/app/policies/acces_point_policy.rb +++ b/app/policies/acces_point_policy.rb @@ -10,11 +10,11 @@ class AccessPointPolicy < ApplicationPolicy end def edit? - organisation_match?(via_referential: true) && user.has_permission?('access_points.edit') + organisation_match? && user.has_permission?('access_points.edit') end def destroy? - organisation_match?(via_referential: true) && user.has_permission?('access_points.destroy') + organisation_match? && user.has_permission?('access_points.destroy') end def update? ; edit? end diff --git a/app/policies/access_link_policy.rb b/app/policies/access_link_policy.rb index 8e7a86490..73b2d1baa 100644 --- a/app/policies/access_link_policy.rb +++ b/app/policies/access_link_policy.rb @@ -10,11 +10,11 @@ class AccessLinkPolicy < ApplicationPolicy end def edit? - organisation_match?(via_referential: true) && user.has_permission?('access_links.edit') + organisation_match? && user.has_permission?('access_links.edit') end def destroy? - organisation_match?(via_referential: true) && user.has_permission?('access_links.destroy') + organisation_match? && user.has_permission?('access_links.destroy') end def update? ; edit? end diff --git a/app/policies/application_policy.rb b/app/policies/application_policy.rb index 07138b38e..4a2d760fb 100644 --- a/app/policies/application_policy.rb +++ b/app/policies/application_policy.rb @@ -1,11 +1,21 @@ class ApplicationPolicy attr_reader :user, :record - def initialize(user, record) - @user = user + def initialize(user_context, record) + @user = user_context.user + @referential = user_context.context[:referential] @record = record end + attr_accessor :referential + def referential + @referential ||= record_referential + end + + def record_referential + record.referential if record.respond_to?(:referential) + end + def index? false end @@ -38,8 +48,14 @@ class ApplicationPolicy Pundit.policy_scope!(user, record.class) end - def organisation_match?(via_referential: false) - eval("user.organisation == record#{'.referential' if via_referential}.organisation") + def organisation_match? + user.organisation == organisation + end + + def organisation + # When sending permission to react UI, we don't have access to record object for edit & destroy.. actions + organisation = record.is_a?(Symbol) ? nil : record.try(:organisation) + organisation or referential.try :organisation end class Scope diff --git a/app/policies/connection_link_policy.rb b/app/policies/connection_link_policy.rb index cc49f575f..abefd741c 100644 --- a/app/policies/connection_link_policy.rb +++ b/app/policies/connection_link_policy.rb @@ -10,11 +10,11 @@ class ConnectionLinkPolicy < ApplicationPolicy end def edit? - organisation_match?(via_referential: true) && user.has_permission?('connection_links.edit') + organisation_match? && user.has_permission?('connection_links.edit') end def destroy? - organisation_match?(via_referential: true) && user.has_permission?('connection_links.destroy') + organisation_match? && user.has_permission?('connection_links.destroy') end def update? ; edit? end diff --git a/app/policies/journey_pattern_policy.rb b/app/policies/journey_pattern_policy.rb index a11fd6bcc..56f32613c 100644 --- a/app/policies/journey_pattern_policy.rb +++ b/app/policies/journey_pattern_policy.rb @@ -6,15 +6,16 @@ class JourneyPatternPolicy < ApplicationPolicy end def create? - user.has_permission?('journey_patterns.create') # organisation match via referential is checked in the view + # organisation match via referential is checked in the view + user.has_permission?('journey_patterns.create') end def edit? - organisation_match?(via_referential: true) && user.has_permission?('journey_patterns.edit') + organisation_match? && user.has_permission?('journey_patterns.edit') end def destroy? - organisation_match?(via_referential: true) && user.has_permission?('journey_patterns.destroy') + organisation_match? && user.has_permission?('journey_patterns.destroy') end def update? ; edit? end diff --git a/app/policies/referential_policy.rb b/app/policies/referential_policy.rb index 1175ba5c6..8d366aff7 100644 --- a/app/policies/referential_policy.rb +++ b/app/policies/referential_policy.rb @@ -6,24 +6,30 @@ class ReferentialPolicy < ApplicationPolicy end def create? - true + user.has_permission?('referentials.create') end def edit? - organisation_match? + organisation_match? && user.has_permission?('referentials.edit') end - def update? - edit? && !record.archived? + def destroy? + organisation_match? && user.has_permission?('referentials.destroy') end def archive? edit? end + def clone? + organisation_match? && create? + end + def unarchive? ; archive? end - def new? ; create? end - def destroy? ; edit? end + def update? ; edit? end + def new? ; create? end end + + diff --git a/app/policies/route_policy.rb b/app/policies/route_policy.rb index 0f42b7f08..c4d048f2a 100644 --- a/app/policies/route_policy.rb +++ b/app/policies/route_policy.rb @@ -10,11 +10,11 @@ class RoutePolicy < ApplicationPolicy end def edit? - organisation_match?(via_referential: true) && user.has_permission?('routes.edit') + organisation_match? && user.has_permission?('routes.edit') end def destroy? - organisation_match?(via_referential: true) && user.has_permission?('routes.destroy') + organisation_match? && user.has_permission?('routes.destroy') end def update? ; edit? end diff --git a/app/policies/routing_constraint_zone_policy.rb b/app/policies/routing_constraint_zone_policy.rb index fbf322066..3126241f0 100644 --- a/app/policies/routing_constraint_zone_policy.rb +++ b/app/policies/routing_constraint_zone_policy.rb @@ -10,11 +10,11 @@ class RoutingConstraintZonePolicy < ApplicationPolicy end def edit? - organisation_match?(via_referential: true) && user.has_permission?('routing_constraint_zones.edit') + organisation_match? && user.has_permission?('routing_constraint_zones.edit') end def destroy? - organisation_match?(via_referential: true) && user.has_permission?('routing_constraint_zones.destroy') + organisation_match? && user.has_permission?('routing_constraint_zones.destroy') end def update? ; edit? end diff --git a/app/policies/time_table_policy.rb b/app/policies/time_table_policy.rb index 1d14c646a..6ca02f451 100644 --- a/app/policies/time_table_policy.rb +++ b/app/policies/time_table_policy.rb @@ -10,11 +10,11 @@ class TimeTablePolicy < ApplicationPolicy end def edit? - organisation_match?(via_referential: true) && user.has_permission?('time_tables.edit') + organisation_match? && user.has_permission?('time_tables.edit') end def destroy? - organisation_match?(via_referential: true) && user.has_permission?('time_tables.destroy') + organisation_match? && user.has_permission?('time_tables.destroy') end def update? ; edit? end diff --git a/app/policies/vehicle_journey_policy.rb b/app/policies/vehicle_journey_policy.rb index 785c2bb1f..ae3680adf 100644 --- a/app/policies/vehicle_journey_policy.rb +++ b/app/policies/vehicle_journey_policy.rb @@ -10,11 +10,11 @@ class VehicleJourneyPolicy < ApplicationPolicy end def edit? - organisation_match?(via_referential: true) && user.has_permission?('vehicle_journeys.edit') + organisation_match? && user.has_permission?('vehicle_journeys.edit') end def destroy? - organisation_match?(via_referential: true) && user.has_permission?('vehicle_journeys.destroy') + organisation_match? && user.has_permission?('vehicle_journeys.destroy') end def update? ; edit? end diff --git a/app/views/access_links/_form.html.slim b/app/views/access_links/_form.html.slim index 463cdf9e3..429656115 100644 --- a/app/views/access_links/_form.html.slim +++ b/app/views/access_links/_form.html.slim @@ -5,7 +5,7 @@ = form.input :stop_area_id , as: :hidden = form.input :link_orientation_type , as: :hidden = form.input :name - = form.input :access_link_type, as: :select, collection: Chouette::AccessLink.access_link_types, include_blank: true, member_label: Proc.new { |type| t("connection_link_types.label.#{type}") } + = form.input :access_link_type, as: :select, collection: access_link_type_label_pairs, include_blank: true = form.input :comment = form.input :link_distance = form.input :mobility_restricted_suitability, as: :select, collection: [[t("true"), true], [t("false"), false]], include_blank: true @@ -21,4 +21,4 @@ = form.actions do = form.action :submit, as: :button - = form.action :cancel, as: :link
\ No newline at end of file + = form.action :cancel, as: :link diff --git a/app/views/access_points/_form.html.slim b/app/views/access_points/_form.html.slim index 1874644d5..5ba7a6863 100644 --- a/app/views/access_points/_form.html.slim +++ b/app/views/access_points/_form.html.slim @@ -7,9 +7,8 @@ = form.input :name = form.input :access_point_type, as: :select, :input_html => {:disabled => !@access_point.new_record? }, - :collection => Chouette::AccessPoint.access_point_types, - include_blank: false, - :member_label => Proc.new { |access_point_type| t("access_types.label.#{access_point_type}") } + :collection => access_point_type_label_pairs, + include_blank: false = form.input :street_name = form.input :country_code = form.input :zip_code @@ -41,4 +40,4 @@ = form.actions do = form.action :submit, as: :button - = form.action :cancel, as: :link
\ No newline at end of file + = form.action :cancel, as: :link diff --git a/app/views/connection_links/_form.html.slim b/app/views/connection_links/_form.html.slim index afef3bba7..a3774ec88 100644 --- a/app/views/connection_links/_form.html.slim +++ b/app/views/connection_links/_form.html.slim @@ -1,9 +1,9 @@ = semantic_form_for [@referential, @connection_link] do |form| = form.inputs do = form.input :name - = form.input :connection_link_type, as: :select, :collection => Chouette::ConnectionLink.connection_link_types, :include_blank => true, :member_label => Proc.new { |type| t("connection_link_types.label.#{type}") } + = form.input :connection_link_type, as: :select, collection: connection_link_type_label_pairs, include_blank: true = form.input :comment - = form.input :link_distance, input_html: { :title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.connection_link.link_distance") } + = form.input :link_distance, input_html: { title: t("formtastic.titles#{format_restriction_for_locales(@referential)}.connection_link.link_distance") } = form.input :mobility_restricted_suitability, as: :select, :collection => [[t("true"), true], [t("false"), false]], include_blank: true = form.input :stairs_availability, as: :select, :collection => [[t("true"), true], [t("false"), false]], include_blank: true = form.input :lift_availability, as: :select, :collection => [[t("true"), true], [t("false"), false]], include_blank: true @@ -18,4 +18,4 @@ = form.actions do = form.action :submit, as: :button - = form.action :cancel, as: :link
\ No newline at end of file + = form.action :cancel, as: :link diff --git a/app/views/errors/not_allowed.html.slim b/app/views/errors/not_allowed.html.slim new file mode 100644 index 000000000..6c94328cc --- /dev/null +++ b/app/views/errors/not_allowed.html.slim @@ -0,0 +1,21 @@ +/ PageHeader += pageheader 'bug', + 'Erreur 403', + '' + +.page_content + .container-fluid + .row + .col-lg-12 + .alert.alert-danger + - if I18n.locale == :fr + p + strong = "Désolé, la page demandée la page n'est pas accessible avec votre profil utilisateur." + + p = "Vous pouvez néanmoins continuer à utiliser l'application IBOO." + + - else + p + strong = "You are not allowed to access the page you were looking for." + + p = "You can still continue the use the IBOO application. Thank you for understanding." diff --git a/app/views/journey_patterns_collections/show.html.slim b/app/views/journey_patterns_collections/show.html.slim index 33e13e3bb..b5607090f 100644 --- a/app/views/journey_patterns_collections/show.html.slim +++ b/app/views/journey_patterns_collections/show.html.slim @@ -11,9 +11,10 @@ .col-lg-12 #journey_patterns - = javascript_tag do | window.stopPoints = #{(@stop_points_list.to_json).html_safe}; | window.journeyPatternLength = #{@journey_patterns.total_entries()}; - | window.journeyPatternsPerPage = #{@ppage} + | window.journeyPatternsPerPage = #{@ppage}; + | window.perms = #{raw @perms} + = javascript_include_tag 'es6_browserified/journey_patterns/index.js' diff --git a/app/views/lines/index.html.slim b/app/views/lines/index.html.slim index ce4930d92..d7b6be5c8 100644 --- a/app/views/lines/index.html.slim +++ b/app/views/lines/index.html.slim @@ -20,6 +20,7 @@ { 'Oid' => Proc.new { |n| n.objectid.local_id }, :number => 'number', :name => 'name', + :deactivated => Proc.new{|n| n.deactivated? ? t('false') : t('true')}, 'networks.name' => Proc.new { |n| n.try(:network).try(:name) }, 'companies.name' => Proc.new { |n| n.try(:company).try(:name) }, :transport_mode => Proc.new { |n| n.transport_mode.nil? ? '-' : t("enumerize.line.transport_mode.#{n.try(:transport_mode)}") }, diff --git a/app/views/networks/_form.html.slim b/app/views/networks/_form.html.slim index f7b97c27d..7b048edc9 100644 --- a/app/views/networks/_form.html.slim +++ b/app/views/networks/_form.html.slim @@ -6,10 +6,10 @@ = form.input :version_date, as: :date_picker = form.input :description = form.input :source_name - = form.input :source_type_name, as: :select, :collection => Chouette::Network.source_type_names, :include_blank => true, :member_label => Proc.new { |mode| t("source_types.label.#{mode}") } + = form.input :source_type_name, as: :select, :collection => source_type_name_label_pairs, :include_blank => true = form.input :source_identifier = form.input :objectid, :required => !@network.new_record?, :input_html => { :title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.network.objectid")} = form.actions do = form.action :submit, as: :button - = form.action :cancel, as: :link
\ No newline at end of file + = form.action :cancel, as: :link diff --git a/app/views/referential_networks/_form.html.slim b/app/views/referential_networks/_form.html.slim index e7020ef68..a92fc7121 100644 --- a/app/views/referential_networks/_form.html.slim +++ b/app/views/referential_networks/_form.html.slim @@ -1,15 +1,15 @@ = semantic_form_for [@referential, @network] do |form| = form.inputs do - = form.input :name, :input_html => { :title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.network.name")} - = form.input :registration_number, :input_html => { :title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.network.registration_number")} + = form.input :name, input_html: { title: t("formtastic.titles#{format_restriction_for_locales(@referential)}.network.name")} + = form.input :registration_number, input_html: { title: t("formtastic.titles#{format_restriction_for_locales(@referential)}.network.registration_number")} = form.input :comment = form.input :version_date, as: :date_picker = form.input :description = form.input :source_name - = form.input :source_type_name, as: :select, :collection => Chouette::Network.source_type_names, :include_blank => true, :member_label => Proc.new { |mode| t("source_types.label.#{mode}") } + = form.input :source_type_name, as: :select, collection: source_type_name_label_pairs, include_blank: true = form.input :source_identifier - = form.input :objectid, :required => !@network.new_record?, :input_html => { :title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.network.objectid")} + = form.input :objectid, required: !@network.new_record?, input_html: { title: t("formtastic.titles#{format_restriction_for_locales(@referential)}.network.objectid")} = form.actions do = form.action :submit, as: :button - = form.action :cancel, as: :link
\ No newline at end of file + = form.action :cancel, as: :link diff --git a/app/views/referentials/index.html.slim b/app/views/referentials/index.html.slim index 8186f725f..8943d419c 100644 --- a/app/views/referentials/index.html.slim +++ b/app/views/referentials/index.html.slim @@ -13,7 +13,6 @@ ul.actions li = link_to 'Données Reflex', stop_area_referential_path(1) li = link_to 'Données CodifLigne', line_referential_path(1) - li= link_to t('calendars.standard_calendars'), calendars_path - / FIXME #823 + li = link_to t('calendars.standard_calendars'), calendars_path - if false li = link_to t('referentials.actions.new'), new_referential_path, class: 'add' diff --git a/app/views/referentials/show.html.slim b/app/views/referentials/show.html.slim index 04f93738e..fd6ebf91a 100644 --- a/app/views/referentials/show.html.slim +++ b/app/views/referentials/show.html.slim @@ -5,10 +5,12 @@ t('last_update', time: l(@referential.updated_at, format: :short)), ((@referential.archived? || !policy(@referential).edit?) ? '' : link_to(t('actions.edit'), edit_referential_path(@referential), class: 'btn btn-default')) do - / Below is secundary actions & optional contents (filters, ...) + / Below is secondary actions & optional contents (filters, ...) .row.mb-sm .col-lg-12.text-right - - if policy(@referential).new? + = link_to t('time_tables.index.title'), referential_time_tables_path(@referential), class: 'btn btn-primary' + + - if policy(@referential).clone? = link_to t('actions.clone'), new_referential_path(from: @referential.id), class: 'btn btn-primary' - if policy(@referential).edit? diff --git a/app/views/routes/show.html.slim b/app/views/routes/show.html.slim index 21a64b61e..9f84b31ad 100644 --- a/app/views/routes/show.html.slim +++ b/app/views/routes/show.html.slim @@ -39,7 +39,6 @@ :deleted_at => Proc.new{|s| s.try(:stop_area).deleted_at ? t('false') : t('true')}, :zip_code => Proc.new {|s| s.try(:stop_area).try(:zip_code)}, :city_name => Proc.new {|s| s.try(:stop_area).try(:city_name)}, - :area_type => Proc.new {|s| t("area_types.label.#{s.try(:stop_area).try(:area_type)}")}, :for_boarding => Proc.new {|s| t("stop_points.stop_point.for_boarding.#{s.for_boarding}")}, :for_alighting => Proc.new {|s| t("stop_points.stop_point.for_alighting.#{s.for_alighting}")}, :position => 'position' }, diff --git a/app/views/routing_constraint_zones/_form.html.slim b/app/views/routing_constraint_zones/_form.html.slim index afc993a0f..b1c77a44b 100644 --- a/app/views/routing_constraint_zones/_form.html.slim +++ b/app/views/routing_constraint_zones/_form.html.slim @@ -4,10 +4,16 @@ = f.input :name .row .col-lg-6.col-sm-12 - / Temporarily limit the collection to 10 items... otherwise it kills RoR - = f.input :stop_area_ids, as: :select, collection: Chouette::StopArea.limit(10), selected: @routing_constraint_zone.stop_area_ids, label: Chouette::StopArea.model_name.human.pluralize.capitalize, label_method: :name, input_html: { 'data-select2ed': 'true', 'data-select2ed-placeholder': 'Sélection de arrêts', 'multiple': 'multiple', style: 'width: 100%' } + = f.input :route_id, collection: @line.routes.select { |route| route.stop_points.count > 2 }, include_blank: false + .row + .col-lg-6.col-sm-12 + - stop_points_collection = @routing_constraint_zone.persisted? ? @routing_constraint_zone.route.stop_points : [] + = f.input :stop_point_ids, as: :select, collection: stop_points_collection, selected: @routing_constraint_zone.stop_point_ids, label: Chouette::StopPoint.model_name.human.pluralize.capitalize, label_method: :name, input_html: { 'data-select2ed': 'true', 'data-select2ed-placeholder': 'Sélection des arrêts sur séquence d\'arrêts', 'multiple': 'multiple', style: 'width: 100%' } .row .col-lg-12.text-right = link_to 'Annuler', :back, class: 'btn btn-link' = f.button :submit, class: 'btn btn-danger' + + += hidden_field_tag 'stop_point_ids', @routing_constraint_zone.stop_point_ids.to_s, id: 'stop_point_ids' diff --git a/app/views/routing_constraint_zones/index.html.slim b/app/views/routing_constraint_zones/index.html.slim index 9061fbdfd..620b0f7db 100644 --- a/app/views/routing_constraint_zones/index.html.slim +++ b/app/views/routing_constraint_zones/index.html.slim @@ -1,11 +1,11 @@ = title_tag Chouette::RoutingConstraintZone.model_name.human.pluralize(:fr) - if policy(Chouette::RoutingConstraintZone).create? && @referential.organisation == current_organisation - = link_to t('routing_constraint_zones.actions.new'), new_referential_line_routing_constraint_zone_path + = link_to t('routing_constraint_zones.actions.new'), new_referential_line_routing_constraint_zone_path(@referential, @line) - if @routing_constraint_zones.any? = table_builder @routing_constraint_zones, - { :name => 'name' }, + { objectid: 'objectid', name: 'name', route: 'route_name', stop_points_count: 'stop_points_count' }, [:show, :edit, :delete], [], 'table table-bordered' diff --git a/app/views/routing_constraint_zones/show.html.slim b/app/views/routing_constraint_zones/show.html.slim index 0f88f5b3f..351784ecc 100644 --- a/app/views/routing_constraint_zones/show.html.slim +++ b/app/views/routing_constraint_zones/show.html.slim @@ -5,14 +5,13 @@ p = @routing_constraint_zone.name p - label => "#{Chouette::Line.model_name.human.capitalize} : " - = link_to @routing_constraint_zone.line.name, referential_line_path(@referential, @line) + label => "#{Chouette::Route.model_name.human.capitalize} : " + = link_to @routing_constraint_zone.route.name, referential_line_route_path(@referential, @line, @routing_constraint_zone.route) p - label => "#{Chouette::StopArea.model_name.human.pluralize.capitalize} : " + label => "#{Chouette::StopPoint.model_name.human.pluralize.capitalize} : " br - - @routing_constraint_zone.stop_areas.each do |stop_area| - = link_to stop_area.name, referential_stop_area_path(@referential, stop_area) + - @routing_constraint_zone.stop_points.each do |stop_point| + = link_to stop_point.name, referential_stop_area_path(@referential, stop_point.stop_area) br - diff --git a/app/views/rule_parameter_sets/_transport_mode_parameter_set_fields.html.slim b/app/views/rule_parameter_sets/_transport_mode_parameter_set_fields.html.slim index a60c966e2..ede7e2b75 100644 --- a/app/views/rule_parameter_sets/_transport_mode_parameter_set_fields.html.slim +++ b/app/views/rule_parameter_sets/_transport_mode_parameter_set_fields.html.slim @@ -1,9 +1,9 @@ = f.inputs class: 'transport_mode_parameter_sets nested-fields' do - = f.input :transport_mode, as: :select, :collection => Chouette::TransportMode.all, include_blank: false, member_label: Proc.new { |mode| t("transport_modes.label.#{mode}") }, label: "transport_mode", wrapper_html: { class: 'fl1' } + = f.input :transport_mode, as: :select, collection: transport_mode_label_pairs, include_blank: false, label: "transport_mode", wrapper_html: { class: 'fl1' } = f.input :inter_stop_area_distance_min, as: :number, label: "inter_stop_area_distance_min", wrapper_html: { class: 'fl1' } = f.input :inter_stop_area_distance_max, as: :number, label: "inter_stop_area_distance_max", wrapper_html: { class: 'fl2' } = f.input :speed_min, as: :number, label: "speed_min", wrapper_html: { class: 'fl2' } = f.input :speed_max, as: :number, label: "speed_max", wrapper_html: { class: 'fl2' } = f.input :inter_stop_duration_variation_max, as: :number, label: "inter_stop_duration_variation_max", wrapper_html: { class: 'fl2' } - = link_to_remove_association t('actions.destroy'), f
\ No newline at end of file + = link_to_remove_association t('actions.destroy'), f diff --git a/app/views/stop_area_copies/new.html.slim b/app/views/stop_area_copies/new.html.slim index a4c0c8bde..1a8764cbc 100644 --- a/app/views/stop_area_copies/new.html.slim +++ b/app/views/stop_area_copies/new.html.slim @@ -8,13 +8,21 @@ - if @stop_area_copy.hierarchy == "child" - if @stop_area.area_type.underscore == "stop_place" - = form.input :area_type, as: :select, :collection => ["stop_place","commercial_stop_point"], :include_blank => false, :member_label => Proc.new { |stop_area_type| t("area_types.label.#{stop_area_type}") } + = form.input :area_type, + as: :select, + collection: label_stop_area_types( "stop_place","commercial_stop_point" ), + include_blank: false - else - = form.input :area_type, as: :select, :collection => ["boarding_position","quay"], :include_blank => false, :member_label => Proc.new { |stop_area_type| t("area_types.label.#{stop_area_type}") } - + = form.input :area_type, + as: :select, + collection: label_stop_area_types( "boarding_position","quay" ), + include_blank: false - else - = form.input :area_type, as: :select, :collection => [@stop_area_copy.area_type], :include_blank => false, :member_label => Proc.new { |stop_area_type| t("area_types.label.#{stop_area_type}") } + = form.input :area_type, + as: :select, + collection: label_stop_area_types(@stop_area_copy.area_type), + include_blank: false = form.actions do = form.action :submit, as: :button , label: t('formtastic.duplicate') - = form.action :cancel, as: :link
\ No newline at end of file + = form.action :cancel, as: :link diff --git a/app/views/stop_areas/index.html.slim b/app/views/stop_areas/index.html.slim index c9ff2cecd..bc1529e68 100644 --- a/app/views/stop_areas/index.html.slim +++ b/app/views/stop_areas/index.html.slim @@ -17,7 +17,7 @@ .row .col-lg-12 = table_builder @stop_areas, - { 'Oid' => Proc.new { |n| n.try(:objectid).try(:local_id) }, + { 'Oid' => Proc.new { |n| n.try(:user_objectid) }, :name => 'name', :registration_number => 'registration_number', :zip_code => 'zip_code', :city_name => 'city_name', :area_type => Proc.new{|s| (s.area_type.nil? ? '-' : t("enumerize.stop_area.area_type.#{s.try(:area_type)}"))} }, [:show, :edit, :delete], diff --git a/app/views/stop_areas/show.html.slim b/app/views/stop_areas/show.html.slim index 9affba08e..58a2dbde3 100644 --- a/app/views/stop_areas/show.html.slim +++ b/app/views/stop_areas/show.html.slim @@ -22,7 +22,7 @@ = definition_list t('metadatas'), { @stop_area.human_attribute_name(:stop_area_type) => t("area_types.label.#{@stop_area.stop_area_type}"), @stop_area.human_attribute_name(:registration_number) => @stop_area.registration_number, - 'Code Reflex' => @stop_area.try(:objectid), + 'Code Reflex' => @stop_area.user_objectid, 'Coordonnées' => geo_data(@stop_area, @stop_area_referential), @stop_area.human_attribute_name(:zip_code) => @stop_area.zip_code, @stop_area.human_attribute_name(:city_name) => @stop_area.city_name, diff --git a/app/views/time_tables/_date_fields.html.slim b/app/views/time_tables/_date_fields.html.slim index f17fcaa2c..1599dd7ff 100644 --- a/app/views/time_tables/_date_fields.html.slim +++ b/app/views/time_tables/_date_fields.html.slim @@ -1,5 +1,14 @@ -= f.inputs class: 'nested-fields date' do - = f.label @time_table.human_attribute_name("date"), class: 'col-md-1' - = f.input :date, as: :date_picker, :label => false, :input_html => { class: 'form-control col-md-3' } - = f.input :in_out, as: :hidden, :input_html => {:value => true} - = link_to_remove_association t('actions.destroy'), f, class: "col-md-3"
\ No newline at end of file +.nested-fields + - if f.object.errors.has_key? :base + .row + .col-lg-12 + .alert.alert-danger + - f.object.errors[:base].each do |message| + p.small = message + + .wrapper + div + = f.input :date, as: :date, label: false, wrapper_html: { class: 'date' } + = f.input :in_out, as: :hidden, :input_html => {:value => true} + div + = link_to_remove_association '', f, class: 'fa fa-trash', data: { confirm: 'Etes-vous sûr(e) ?' }, title: t('actions.delete') diff --git a/app/views/time_tables/_excluded_date_fields.html.slim b/app/views/time_tables/_excluded_date_fields.html.slim index 294d103fc..dba5bf952 100644 --- a/app/views/time_tables/_excluded_date_fields.html.slim +++ b/app/views/time_tables/_excluded_date_fields.html.slim @@ -1,5 +1,15 @@ -= f.inputs class: 'nested-fields date' do - = f.label @time_table.human_attribute_name("date"), class: 'col-md-1' - = f.input :date, as: :date_picker, :label => false, :input_html => { class: 'form-control col-md-3' } - = f.input :in_out, as: :hidden, :input_html => {:value => false} - = link_to_remove_association t('actions.destroy'), f, class: "col-md-3"
\ No newline at end of file +.nested-fields + - if f.object.errors.has_key? :base + .row + .col-lg-12 + .alert.alert-danger + - f.object.errors[:base].each do |message| + p.small = message + + .wrapper + div + / = f.label @time_table.human_attribute_name("date"), class: 'col-md-1' + = f.input :date, as: :date, label: false, wrapper_html: { class: 'date' } + = f.input :in_out, as: :hidden, input_html: {value: false} + div + = link_to_remove_association '', f, class: 'fa fa-trash', data: { confirm: 'Etes-vous sûr(e) ?' }, title: t('actions.delete') diff --git a/app/views/time_tables/_form.html.slim b/app/views/time_tables/_form.html.slim index 8652f7cb5..97df72fd3 100644 --- a/app/views/time_tables/_form.html.slim +++ b/app/views/time_tables/_form.html.slim @@ -1,73 +1,102 @@ -= semantic_form_for [@referential, @time_table] do |form| - = form.inputs do - = form.input :comment, :input_html => { :title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.time_table.comment")} - = form.input :version - = form.input :tag_search, as: :tags, :input_html => { :id => "tag_search",:placeholder => t("formtastic.placeholders.time_table.tag_search") } - = form.input :tag_list, as: :hidden, :input_html => { :id => "tag_list" } - = form.input :objectid, :required => !@time_table.new_record?, :input_html => { :title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.time_table.objectid")} - - if @time_table.new_record? - = form.input :calendar, as: :select, collection: current_organisation.calendars - - h3.time_table_periods = @time_table.human_attribute_name("periods") - - #periods_content - = form.inputs class: 'day_type' do - label.day_type_label = @time_table.human_attribute_name("day_types") - = form.input :monday, as: :boolean, class: "others" - = form.input :tuesday, as: :boolean - = form.input :wednesday, as: :boolean - = form.input :thursday, as: :boolean - = form.input :friday, as: :boolean - = form.input :saturday, as: :boolean - = form.input :sunday, as: :boolean - - #periods - = form.semantic_fields_for :periods do |p| - == render "period_fields", :f => p - - = link_to_add_association t("time_tables.actions.add_period"), form, :periods , :"data-association-insertion-method" => "append", :"data-association-insertion-node" => "div#periods" - - h3.time_table_dates = @time_table.human_attribute_name("dates") - - #dates_content - #dates - = form.semantic_fields_for :dates, @time_table.dates.to_a.select {|d| d.in_out == true} do |p| - == render "date_fields", :f => p - - = link_to_add_association t("time_tables.actions.add_date"), form, :dates, :"data-association-insertion-method" => "append", :"partial" => "date_fields", :"data-association-insertion-node" => "div#dates" - - h3.time_table_dates = @time_table.human_attribute_name("excluded_dates") - - #excluded_dates_content - #excluded_dates - = form.semantic_fields_for :dates, @time_table.dates.to_a.select {|d| d.in_out == false} do |p| - == render "excluded_date_fields", :f => p - - = link_to_add_association t("time_tables.actions.add_excluded_date"), form, :dates, :"data-association-insertion-method" => "append", :"partial" => "excluded_date_fields", :"data-association-insertion-node" => "div#excluded_dates" - - = form.actions do - = form.action :submit, as: :button - = form.action :cancel, as: :link - -= javascript_tag "var items = #{ @time_table.tag_list.to_a };" - -javascript: - $("#tag_search").tagsManager({ - prefilled: items, - output: '#tag_list', - tagsContainer: '#tagsContainer' - }); - - var time_tables_tag_list = new Bloodhound({ - datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'), - queryTokenizer: Bloodhound.tokenizers.whitespace, - remote: "#{tags_referential_time_tables_path(@referential, format: 'json')}?tag=%QUERY", - }); - - time_tables_tag_list.initialize(); - - $("#tag_search").typeahead(null, { - name: 'time_tables_tag_list', - displayKey: 'name', - source: time_tables_tag_list.ttAdapter() - }); += simple_form_for [@referential, @time_table], html: {class: 'form-horizontal', id: 'timetable_form'}, wrapper: :horizontal_form do |form| + + .row + .col-lg-12 + = form.input :comment, :input_html => { :title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.time_table.comment")} + + .form-group + = form.label @time_table.human_attribute_name(:color), required: false, class: 'control-label col-sm-4' + + .col-sm-8 + .dropdown.color_selector + button.btn.btn-default.dropdown-toggle type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true" + span.fa.fa-circle.mr-xs style="color:#{@time_table.color.nil? ? 'transparent' : @time_table.color}" + span.caret + + = form.input :color, as: :radio_buttons, label: false, collection: ["", "#9B9B9B", "#FFA070", "#C67300", "#7F551B", "#41CCE3", "#09B09C", "#3655D7", "#6321A0", "#E796C6", "#DD2DAA"], input_html: {class: 'color_selector'}, label_method: lambda{|c| ("<span class='fa fa-circle' style='color:" + (c.empty? ? 'transparent' : c) + "'></span>").html_safe}, wrapper_html: { class: 'dropdown-menu', 'aria-labelledby': "dropdownMenu1"}, include_blank: true + + / = form.input :tag_list, as: :tags + + .form-group + label.control-label.col-sm-4 + = "Journées d'applications pour les périodes ci-dessous" + + .col-sm-8 + .form-group.labelled-checkbox-group + = form.input :monday, as: :boolean, label: ("<span class='lcbx-group-item-label'>L</span>").html_safe, wrapper_html: { class: 'lcbx-group-item' } + = form.input :tuesday, as: :boolean, label: ("<span class='lcbx-group-item-label'>Ma</span>").html_safe, wrapper_html: { class: 'lcbx-group-item' } + = form.input :wednesday, as: :boolean, label: ("<span class='lcbx-group-item-label'>Me</span>").html_safe, wrapper_html: { class: 'lcbx-group-item' } + = form.input :thursday, as: :boolean, label: ("<span class='lcbx-group-item-label'>J</span>").html_safe, wrapper_html: { class: 'lcbx-group-item' } + = form.input :friday, as: :boolean, label: ("<span class='lcbx-group-item-label'>V</span>").html_safe, wrapper_html: { class: 'lcbx-group-item' } + = form.input :saturday, as: :boolean, label: ("<span class='lcbx-group-item-label'>S</span>").html_safe, wrapper_html: { class: 'lcbx-group-item' } + = form.input :sunday, as: :boolean, label: ("<span class='lcbx-group-item-label'>D</span>").html_safe, wrapper_html: { class: 'lcbx-group-item' } + + - if @time_table.new_record? + = form.input :calendar, as: :select, collection: current_organisation.calendars + + .separator + + .row + .col-lg-8.col-lg-offset-4 + .subform + .nested-head + .wrapper + div + .form-group + label.control-label.required + = t('simple_form.labels.referential.metadatas.periods.begin') + abbr title='requis' * + div + .form-group + label.control-label.required + = t('simple_form.labels.referential.metadatas.periods.end') + abbr title='requis' * + div + + = form.simple_fields_for :periods do |p| + = render "period_fields", f: p + + .links.nested-linker + = link_to_add_association t("time_tables.actions.add_period"), form, :periods, class: 'btn btn-outline-primary' + + .row + .col-lg-12.mb-sm.mt-md + #time_tables + .alert.alert-warning + |Les éléments ci-dessous sont à supprimer. + + .row + .col-lg-6.col-md-6.col-sm-12.col-xs-12 + .subform + .nested-head + .wrapper + div + .form-group + label.control-label + = @time_table.human_attribute_name("dates") + div + + = form.simple_fields_for :dates, @time_table.dates.to_a.select {|d| d.in_out == true} do |p| + = render "date_fields", f: p + + .links.nested-linker + = link_to_add_association t("time_tables.actions.add_date"), form, :dates, partial: 'date_fields', class: 'btn btn-outline-primary' + + .col-lg-6.col-md-6.col-sm-12.col-xs-12 + .subform + .nested-head + .wrapper + div + .form-group + label.control-label + = @time_table.human_attribute_name("excluded_dates") + div + + = form.simple_fields_for :dates, @time_table.dates.to_a.select {|d| d.in_out == false} do |q| + = render "excluded_date_fields", f: q + + .links.nested-linker + = link_to_add_association t("time_tables.actions.add_excluded_date"), form, :dates, partial: 'excluded_date_fields', class: 'btn btn-outline-primary' + + + = form.button :submit, t('actions.submit'), class: 'btn btn-default formSubmitr', form: 'timetable_form' diff --git a/app/views/time_tables/_period_fields.html.slim b/app/views/time_tables/_period_fields.html.slim index cefa68df5..f879ded00 100644 --- a/app/views/time_tables/_period_fields.html.slim +++ b/app/views/time_tables/_period_fields.html.slim @@ -1,8 +1,15 @@ -= f.inputs class: 'nested-fields period' do - = f.label @time_table.human_attribute_name("period_start"), class: "col-md-1" - = f.input :period_start, as: :date_picker, :label => false, :input_html => { class: 'form-control col-md-3' } - - = f.label @time_table.human_attribute_name("period_end"), class: "col-md-1" - = f.input :period_end, as: :date_picker, :label => false, :input_html => { class: 'form-control col-md-3' } - - = link_to_remove_association t('actions.destroy'), f, class: "col-md-2"
\ No newline at end of file +.nested-fields + - if f.object.errors.has_key? :base + .row + .col-lg-12 + .alert.alert-danger + - f.object.errors[:base].each do |message| + p.small = message + + .wrapper + div + = f.input :period_start, as: :date, label: false, wrapper_html: { class: 'date' } + div + = f.input :period_end, as: :date, label: false, wrapper_html: { class: 'date' } + div + = link_to_remove_association '', f, class: 'fa fa-trash', data: { confirm: 'Etes-vous sûr(e) ?' }, title: t('actions.delete') diff --git a/app/views/time_tables/_periods.html.slim b/app/views/time_tables/_periods.html.slim deleted file mode 100644 index e3c6d5f39..000000000 --- a/app/views/time_tables/_periods.html.slim +++ /dev/null @@ -1,5 +0,0 @@ -ul.periods - - @time_table.periods.each do |tmp| - li.period - = "#{('time_tables.show.from')} #{l tmp.period_start}" - = "#{t('time_tables.show.to')} #{l tmp.period_end}"
\ No newline at end of file diff --git a/app/views/time_tables/_show_time_table.html.slim b/app/views/time_tables/_show_time_table.html.slim index 419d13c96..ebfe9d283 100644 --- a/app/views/time_tables/_show_time_table.html.slim +++ b/app/views/time_tables/_show_time_table.html.slim @@ -1,94 +1,25 @@ -#time_table_show.time_table_show - p - span class="state-code #{@time_table.presenter.time_table_state_code}" - i.fa.fa-certificate - - label - - if @time_table.bounding_dates.empty? - = t(".resume_empty") - - else - = t(".resume", :start_date => l(@time_table.bounding_dates.min), :end_date => l(@time_table.bounding_dates.max)) - - p - label = "#{@time_table.human_attribute_name('tag_list')} : " - = @time_table.tag_list - - ul.nav.nav-tabs id="tabs" data-tabs="tabs" - li.active - a href="#time_tables" data-toggle="tab" - = @time_table.human_attribute_name("calendars") - - li - a href="#time_tables_datas" data-toggle="tab" - = @time_table.human_attribute_name("calendar_details") - - #my-tab-content.tab-content - #time_tables.tab-pane.active - #associated_calendar - => "#{t('calendars.standard_calendar')} : " - - if @time_table.calendar - = link_to @time_table.calendar.name, @time_table.calendar - - else - = '--' - - .well.legend - span.title = t(".legend") - span.label.excluded_date X - = t(".excluded_date") - span.label.overlaped_date X - = t(".overlap_date") - span.label.selected_date X - = t(".selected_date") - span.label.selected_period X - = t(".selected_period") - - #calendars - .year_choice - span.previous = link_to("<", referential_time_table_path(@referential, @time_table, year: (@year - 1)) ) - span.year = "#{@year}" - span.next = link_to(">", referential_time_table_path(@referential, @time_table, year: (@year + 1)) ) - - .calendar_helper - - cal = "" - - (1..12).each do |month| - - cal << calendar(year: @year, month: month, first_day_of_week: 1) do |d| - - if @time_table.excluded_date?(d) - - [link_to(d.mday, edit_referential_time_table_path(@referential, @time_table) ), {class: "day excluded_date"}] - - elsif @time_table.include_in_overlap_dates?(d) - - [link_to(d.mday, edit_referential_time_table_path(@referential, @time_table) ), {class: "day overlaped_date"}] - - elsif @time_table.include_in_dates?(d) - - [link_to(d.mday, edit_referential_time_table_path(@referential, @time_table) ), {class: "day selected_date"}] - - elsif @time_table.include_in_periods?(d) - - [link_to(d.mday, edit_referential_time_table_path(@referential, @time_table) ), {class: "day selected_period"}] - - = cal.html_safe - - #time_tables_datas.tab-pane - .summary - p - label = "#{@time_table.human_attribute_name('version')} : " - = @time_table.version - - p - label = "#{@time_table.human_attribute_name('day_types')} : " - - if @time_table.int_day_types & 508 == 0 - label = "#{@time_table.human_attribute_name('none')} : " - - else - - %w(monday tuesday wednesday thursday friday saturday sunday).each do |day_type| - span class="#{@time_table.send(day_type) ? 'included_day_type' :'excluded_day_type'}" - = @time_table.human_attribute_name(day_type) - - - if @time_table.periods.present? - h3.time_table_periods = @time_table.human_attribute_name("periods") - .periods.content - == render 'time_tables/periods' - - - if @time_table.dates.where("in_out = true").present? - h3.time_table_dates = @time_table.human_attribute_name("dates") - .dates.content - == render "time_tables/dates" - - - if @time_table.dates.where("in_out = false").present? - h3.time_table_dates = @time_table.human_attribute_name("excluded_dates") - .excluded_dates.content - == render "time_tables/excluded_dates" +.row + - (1..12).each do |month| + .col-lg-3.col-md-4.col-sm-4.col-xs-6 + = new_alt_calendar(year: @year, month: month, first_day_of_week: 1, calendar_title: "#{I18n.t("date.month_names")[month]}", show_today: false) do |d| + / - if @time_table.excluded_date?(d) + / - [link_to(d.mday, edit_referential_time_table_path(@referential, @time_table) ), {class: "day excluded_date"}] + - if @time_table.include_in_overlap_dates?(d) + - [link_to(d.mday, edit_referential_time_table_path(@referential, @time_table) ), {class: "day overlaped_date", title: 'Voir'}] + - elsif @time_table.include_in_dates?(d) + - [link_to(d.mday, edit_referential_time_table_path(@referential, @time_table) ), {class: "day selected_date", title: 'Voir'}] + - elsif @time_table.include_in_periods?(d) + - [link_to(d.mday, edit_referential_time_table_path(@referential, @time_table) ), {class: "day selected_period", title: 'Voir'}] + +/ .row +/ .col-lg-12 +/ / wip +/ - if @time_table.dates.where("in_out = true").present? +/ h3.time_table_dates = @time_table.human_attribute_name("dates") +/ .dates.content +/ == render "time_tables/dates" +/ +/ - if @time_table.dates.where("in_out = false").present? +/ h3.time_table_dates = @time_table.human_attribute_name("excluded_dates") +/ .excluded_dates.content +/ == render "time_tables/excluded_dates" diff --git a/app/views/time_tables/edit.html.slim b/app/views/time_tables/edit.html.slim index 1746b48c7..7f4cd18df 100644 --- a/app/views/time_tables/edit.html.slim +++ b/app/views/time_tables/edit.html.slim @@ -1,3 +1,12 @@ -= title_tag t('time_tables.edit.title', :time_table => @time_table.comment) +/ PageHeader += pageheader 'map-marker', + @time_table.comment, + '', + '' -== render 'form'
\ No newline at end of file +/ PageContent +.page_content + .container-fluid + #periods + += javascript_include_tag 'es6_browserified/time_tables/index.js' diff --git a/app/views/time_tables/index.html.slim b/app/views/time_tables/index.html.slim index 64d2372a5..65d0787e8 100644 --- a/app/views/time_tables/index.html.slim +++ b/app/views/time_tables/index.html.slim @@ -1,37 +1,51 @@ -= title_tag t('time_tables.index.title') - -= search_form_for @q, :url => referential_time_tables_path(@referential), remote: true, :html => {:method => :get, class: "form-inline", :id => "search", role: "form"} do |f| - .panel.panel-default - .panel-heading - .input-group.col-md-9 - = f.text_field :comment_cont, :placeholder => "#{t('.comment')}", class: 'form-control' - - .input-group-btn - button.btn.btn-default type="submit" - i.fa.fa-search - - a data-toggle="collapse" data-parent="#search" href="#advanced_search" - i.fa.fa-plus - = "#{t('.advanced_search')}" - - #advanced_search.panel-collapse.collapse - .panel-body - div - label = "#{t('.from')}" - = f.text_field :start_date_gteq, :placeholder => "#{t('.start_date')}", class: 'form-control date_picker', :type => "date" - - label = "#{t('.to')}" - = f.text_field :end_date_lteq, :placeholder => "#{t('.end_date')}", class: 'form-control date_picker', :type => "date" - - div - = f.text_field :tag_search, :placeholder => "#{t('.tag_search')}", class: 'form-control' - -#time_tables - == render 'time_tables' - -- content_for :sidebar do - ul.actions - li - - if policy(Chouette::TimeTable).create? && @referential.organisation == current_organisation - = link_to t('time_tables.actions.new'), new_referential_time_table_path(@referential), class: "add" - br +/ PageHeader += pageheader 'map-marker', + t('time_tables.index.title'), + '', + ((policy(Chouette::TimeTable).create? && @referential.organisation == current_organisation) ? link_to(t('actions.add'), new_referential_time_table_path(@referential), class: 'btn btn-default') : '') + +/ PageContent +.page_content + .container-fluid + .row + .col-lg-12 + = search_form_for @q, url: referential_time_tables_path(@referential), html: { method: :get, class: 'form form-filter' } do |f| + .ffg-row + .input-group.search_bar + = f.text_field :comment_cont, :placeholder => "#{t('.comment')}", class: 'form-control' + span.input-group-btn + button.btn.btn-default type='submit' + span.fa.fa-search + + .ffg-row + .form-group + = f.label @time_tables.human_attribute_name(:tag_search), required: false, class: 'control-label' + = f.input :tag_search, as: :tags, collection: Chouette::TimeTable.tags_on(:tags).pluck(:name), label: false, input_html: { 'data-select2ed': 'true', 'data-select2ed-placeholder': 'Indiquez une étiquette...' }, wrapper_html: { class: 'select2ed'}, include_blank: false + + .form-group.togglable + = f.label @time_tables.human_attribute_name(:bounding_dates), required: false, class: 'control-label' + .filter_menu + = f.input :start_date_gteq, as: :date, label: t('simple_form.from'), wrapper_html: { class: 'date filter_menu-item' } + = f.input :end_date_lteq, as: :date, label: t('simple_form.to'), wrapper_html: { class: 'date filter_menu-item' } + + + .actions + = link_to 'Effacer', @workbench, class: 'btn btn-link' + = f.submit 'Filtrer', class: 'btn btn-default' + + - if @time_tables.any? + .row + .col-lg-12 + = table_builder @time_tables, + { :comment => 'comment', :color => Proc.new{|tt| tt.color ? content_tag(:span, '', class: 'fa fa-circle', style: "color:#{tt.color}") : '-' }, + "Période de validité englobante" => Proc.new{ |tt| tt.bounding_dates.empty? ? '-' : t('bounding_dates', debut: l(tt.bounding_dates.min), end: l(tt.bounding_dates.max)) }, :calendar => Proc.new{ |tt| tt.calendar ? tt.calendar.try(:name) : '-' }, :updated_at => Proc.new {|tt| l(tt.updated_at, format: :short)} }, + [:show, :edit, :duplicate, :delete], + [], + 'table has-search' + + = new_pagination @time_tables, 'pull-right' + + - unless @time_tables.any? + .row.mt-xs + .col-lg-12 + = replacement_msg t('time_tables.search_no_results') diff --git a/app/views/time_tables/index.js.slim b/app/views/time_tables/index.js.slim deleted file mode 100644 index bc9585c4b..000000000 --- a/app/views/time_tables/index.js.slim +++ /dev/null @@ -1 +0,0 @@ -| $('#time_tables').html("#{escape_javascript(render('time_tables'))}");
\ No newline at end of file diff --git a/app/views/time_tables/index.json.rabl b/app/views/time_tables/index.json.rabl deleted file mode 100644 index 443cb13a2..000000000 --- a/app/views/time_tables/index.json.rabl +++ /dev/null @@ -1,9 +0,0 @@ -collection @time_tables, :object_root => false - -node do |time_table| - { :id => time_table.id, :comment => time_table.comment, - :time_table_bounding => time_table_bounding( time_table), - :composition_info => composition_info(time_table), - :tags => time_table.tags.join(','), - :day_types => %w(monday tuesday wednesday thursday friday saturday sunday).select{ |d| time_table.send(d) }.map{ |d| time_table.human_attribute_name(d).first(2)}.join('')} -end diff --git a/app/views/time_tables/index.rabl b/app/views/time_tables/index.rabl new file mode 100644 index 000000000..d8b7c6e0c --- /dev/null +++ b/app/views/time_tables/index.rabl @@ -0,0 +1,2 @@ +collection @time_tables, :object_root => false +extends "time_tables/show" diff --git a/app/views/time_tables/month.rabl b/app/views/time_tables/month.rabl new file mode 100644 index 000000000..5b8b67f6c --- /dev/null +++ b/app/views/time_tables/month.rabl @@ -0,0 +1,9 @@ +object @time_table + +node do |tt| + { + name: I18n.l(@date, format: '%B'), + days: tt.month_inspect(@date), + day_types: %w(monday tuesday wednesday thursday friday saturday sunday).select{ |d| tt.send(d) }.map{ |d| tt.human_attribute_name(d).first(2)}.join('') + } +end diff --git a/app/views/time_tables/new.html.slim b/app/views/time_tables/new.html.slim index bc15f7032..8770a59b2 100644 --- a/app/views/time_tables/new.html.slim +++ b/app/views/time_tables/new.html.slim @@ -1,3 +1,12 @@ -= title_tag t('time_tables.new.title') +/ PageHeader += pageheader 'map-marker', + t('time_tables.new.title'), + '', + '' -== render 'form'
\ No newline at end of file +/ PageContent +.page_content + .container-fluid + .row + .col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-10.col-sm-offset-1 + == render 'form' diff --git a/app/views/time_tables/show.html.slim b/app/views/time_tables/show.html.slim index 436886faa..6e6833cf4 100644 --- a/app/views/time_tables/show.html.slim +++ b/app/views/time_tables/show.html.slim @@ -1,27 +1,51 @@ - require 'calendar_helper' -= title_tag t('time_tables.show.title', :time_table => @time_table.comment ) +/ PageHeader += pageheader 'map-marker', + @time_table.comment, + '', + (policy(@time_table).edit? ? link_to(t('actions.edit'), edit_referential_time_table_path(@referential, @time_table), class: 'btn btn-default') : '') -== render 'time_table_combinations/combine' + / Below is secundary actions & optional contents (filters, ...) + .row.mb-sm + .col-lg-12.text-right + / - if policy(@time_table).create? && @referential.organisation == current_organisation + / = link_to t('time_tables.actions.new'), new_referential_time_table_path(@referential), class: 'btn btn-primary' -== render 'show_time_table' + /- if policy(@time_table).create? && @referential.organisation == current_organisation + = link_to t('actions.combine'), new_referential_time_table_time_table_combination_path(@referential, @time_table), {remote: true, 'data-toggle' => "modal", 'data-target' => '#modal_combine', class: 'btn btn-primary' } -- content_for :sidebar do - ul.actions - li - if policy(@time_table).create? && @referential.organisation == current_organisation - = link_to t('time_tables.actions.new'), new_referential_time_table_path(@referential), class: 'add' - li - - if policy(@time_table).edit? - = link_to t('time_tables.actions.edit'), edit_referential_time_table_path(@referential, @time_table), class: "edit" - li + = link_to t('actions.clone'), duplicate_referential_time_table_path(@referential, @time_table), class: 'btn btn-primary' + - if policy(@time_table).destroy? - = link_to t('time_tables.actions.destroy'), referential_time_table_path(@referential, @time_table), :method => :delete, :data => {:confirm => t('time_tables.actions.destroy_confirm')}, class: "remove" - li - - if policy(@time_table).create? && @referential.organisation == current_organisation - = link_to t('time_tables.actions.duplicate'), duplicate_referential_time_table_path(@referential, @time_table), class: "clone" - li - /- if policy(@time_table).create? && @referential.organisation == current_organisation - = link_to t('time_tables.actions.combine'), new_referential_time_table_time_table_combination_path(@referential, @time_table), {:remote => true, 'data-toggle' => "modal", 'data-target' => '#modal_combine', class: "merge"} + = link_to referential_time_table_path(@referential, @time_table), method: :delete, data: {confirm: t('time_tables.actions.destroy_confirm')}, class: 'btn btn-primary' do + span.fa.fa-trash + span = t('actions.destroy') + +/ PageContent +.page_content + .container-fluid + .row + .col-lg-6.col-md-6.col-sm-12.col-xs-12 + = definition_list t('metadatas'), + { "Période d'application" => (@time_table.bounding_dates.empty? ? '-' : t('bounding_dates', debut: l(@time_table.bounding_dates.min), end: l(@time_table.bounding_dates.max))), + 'Couleur associée' => content_tag(:span, '', class: 'fa fa-circle', style: "color:#{@time_table.try(:color)}"), + 'Etiquettes' => @time_table.tag_list, + 'Modèle de calendrier' => (@time_table.calendar ? link_to(@time_table.calendar.name, @time_table.calendar) : '-'), + "Journées d'application pour les périodes ci-dessous" => %w(monday tuesday wednesday thursday friday saturday sunday).collect{ |d| content_tag(:span, t("calendars.days.#{d}"), class: "label label-default #{@time_table.send(d) ? '' : 'disabled'}") }.join.html_safe } + + .row + .col-lg-12.mb-sm + .pagination.pull-right + = @year + .page_links + = link_to '', referential_time_table_path(@referential, @time_table, year: (@year - 1)), class: 'previous_page' + = link_to '', referential_time_table_path(@referential, @time_table, year: (@year + 1)), class: 'next_page' + + = render 'show_time_table' - = creation_tag(@time_table) + .row + .col-lg-12 + / WTF ??! + = render 'time_table_combinations/combine' diff --git a/app/views/time_tables/show.rabl b/app/views/time_tables/show.rabl new file mode 100644 index 000000000..a0a799985 --- /dev/null +++ b/app/views/time_tables/show.rabl @@ -0,0 +1,26 @@ +object @time_table + +attributes :id, :comment +node do |tt| + { + time_table_bounding: tt.presenter.time_table_bounding, + tags: tt.tags.map{ |tag| {id: tag.id, name: tag.name}}, + day_types: %w(monday tuesday wednesday thursday friday saturday sunday).select{ |d| tt.send(d) }.map{ |d| tt.human_attribute_name(d).first(2)}.join(''), + current_month: tt.month_inspect(Date.today), + periode_range: month_periode_enum(3), + current_periode_range: Date.today.beginning_of_month, + color: tt.color ? tt.color : '' + } +end + +child(:periods, object_root: false) do + attributes :id, :period_start, :period_end +end + +child(:dates, object_root: false) do + attributes :id, :date, :in_out +end + +child(:calendar) do + attributes :id, :name +end diff --git a/app/views/vehicle_journeys/_form.html.slim b/app/views/vehicle_journeys/_form.html.slim index c738b8b00..ca200a5f7 100644 --- a/app/views/vehicle_journeys/_form.html.slim +++ b/app/views/vehicle_journeys/_form.html.slim @@ -2,7 +2,7 @@ == render 'shared/form_messages', { errors: vehicle_journey.errors } = form.inputs do - = form.input :journey_pattern, as: :select, :collection => @route.journey_patterns, :member_label => Proc.new { |jp| journey_name(jp) } + = form.input :journey_pattern, as: :select, collection: route_journey_pattern_label_pairs(@route) = form.input :number = form.input :published_journey_name = form.input :published_journey_identifier diff --git a/app/views/vehicle_journeys/show.rabl b/app/views/vehicle_journeys/show.rabl index 86edfafa8..6b7608342 100644 --- a/app/views/vehicle_journeys/show.rabl +++ b/app/views/vehicle_journeys/show.rabl @@ -8,6 +8,12 @@ child(:company) do |company| attributes :id, :objectid, :name end +child(:route) do |route| + child(:line) do |line| + attributes :transport_mode, :transport_submode + end +end + child(:journey_pattern) do |journey_pattern| attributes :id, :objectid, :name, :published_name end @@ -29,6 +35,9 @@ child(:vehicle_journey_at_stops_matrix, :object_root => false) do |vehicle_stops node(:stop_area_object_id) do vehicle_stop.stop_point ? vehicle_stop.stop_point.stop_area.objectid : nil end + node(:stop_point_objectid) do + vehicle_stop.stop_point ? vehicle_stop.stop_point.objectid : nil + end node(:stop_area_name) do vehicle_stop.stop_point ? vehicle_stop.stop_point.stop_area.name : nil end diff --git a/app/views/workbenches/show.html.slim b/app/views/workbenches/show.html.slim index 2d13501b7..1025c1658 100644 --- a/app/views/workbenches/show.html.slim +++ b/app/views/workbenches/show.html.slim @@ -7,7 +7,9 @@ / Below is secundary actions & optional contents (filters, ...) .row.mb-sm .col-lg-12.text-right - = link_to t('referentials.actions.new'), new_referential_path(workbench_id: @workbench), class: 'btn btn-primary' + = link_to Import.model_name.human.pluralize.capitalize, workbench_imports_path(@workbench), class: 'btn btn-primary' + - if policy(Referential).create? + = link_to t('referentials.actions.new'), new_referential_path(workbench_id: @workbench), class: 'btn btn-primary' / PageContent .page_content diff --git a/app/workers/referential_cloning_worker.rb b/app/workers/referential_cloning_worker.rb index dda569d7c..ef3acd529 100644 --- a/app/workers/referential_cloning_worker.rb +++ b/app/workers/referential_cloning_worker.rb @@ -1,24 +1,33 @@ class ReferentialCloningWorker include Sidekiq::Worker + sidekiq_options queue: 'wip' + # Replace default apartment created schema with clone schema from source referential def perform(id) - # Replace default apartment created schema with clone schema from source referential ref_cloning = ReferentialCloning.find id - sql_func = "CREATE OR REPLACE FUNCTION clone_schema( source_schema text, dest_schema text, include_recs boolean) RETURNS void AS $BODY$ DECLARE src_oid oid; tbl_oid oid; func_oid oid; object text; buffer text; srctbl text; default_ text; column_ text; qry text; dest_qry text; v_def text; seqval bigint; sq_last_value bigint; sq_max_value bigint; sq_start_value bigint; sq_increment_by bigint; sq_min_value bigint; sq_cache_value bigint; sq_log_cnt bigint; sq_is_called boolean; sq_is_cycled boolean; sq_cycled char(10); BEGIN SELECT oid INTO src_oid FROM pg_namespace WHERE nspname = quote_ident(source_schema); IF NOT FOUND THEN RAISE NOTICE 'source schema % does not exist!', source_schema; RETURN ; END IF; PERFORM nspname FROM pg_namespace WHERE nspname = quote_ident(dest_schema); IF FOUND THEN RAISE NOTICE 'dest schema % already exists!', dest_schema; RETURN ; END IF; EXECUTE 'CREATE SCHEMA ' || quote_ident(dest_schema) ; FOR object IN SELECT sequence_name::text FROM information_schema.sequences WHERE sequence_schema = quote_ident(source_schema) LOOP EXECUTE 'CREATE SEQUENCE ' || quote_ident(dest_schema) || '.' || quote_ident(object); srctbl := quote_ident(source_schema) || '.' || quote_ident(object); EXECUTE 'SELECT last_value, max_value, start_value, increment_by, min_value, cache_value, log_cnt, is_cycled, is_called FROM ' || quote_ident(source_schema) || '.' || quote_ident(object) || ';' INTO sq_last_value, sq_max_value, sq_start_value, sq_increment_by, sq_min_value, sq_cache_value, sq_log_cnt, sq_is_cycled, sq_is_called ; IF sq_is_cycled THEN sq_cycled := 'CYCLE'; ELSE sq_cycled := 'NO CYCLE'; END IF; EXECUTE 'ALTER SEQUENCE ' || quote_ident(dest_schema) || '.' || quote_ident(object) || ' INCREMENT BY ' || sq_increment_by || ' MINVALUE ' || sq_min_value || ' MAXVALUE ' || sq_max_value || ' START WITH ' || sq_start_value || ' RESTART ' || sq_min_value || ' CACHE ' || sq_cache_value || sq_cycled || ' ;' ; buffer := quote_ident(dest_schema) || '.' || quote_ident(object); IF include_recs THEN EXECUTE 'SELECT setval( ''' || buffer || ''', ' || sq_last_value || ', ' || sq_is_called || ');' ; ELSE EXECUTE 'SELECT setval( ''' || buffer || ''', ' || sq_start_value || ', ' || sq_is_called || ');' ; END IF; END LOOP; FOR object IN SELECT TABLE_NAME::text FROM information_schema.tables WHERE table_schema = quote_ident(source_schema) AND table_type = 'BASE TABLE' LOOP buffer := dest_schema || '.' || quote_ident(object); EXECUTE 'CREATE TABLE ' || buffer || '(LIKE ' || quote_ident(source_schema) || '.' || quote_ident(object) || ' INCLUDING ALL)'; IF include_recs THEN EXECUTE 'INSERT INTO ' || buffer || ' SELECT * FROM ' || quote_ident(source_schema) || '.' || quote_ident(object) || ';'; END IF; FOR column_, default_ IN SELECT column_name::text, REPLACE(column_default::text, source_schema, dest_schema) FROM information_schema.COLUMNS WHERE table_schema = dest_schema AND TABLE_NAME = object AND column_default LIKE 'nextval(%' || quote_ident(source_schema) || '%::regclass)' LOOP EXECUTE 'ALTER TABLE ' || buffer || ' ALTER COLUMN ' || column_ || ' SET DEFAULT ' || default_; END LOOP; END LOOP; FOR qry IN SELECT 'ALTER TABLE ' || quote_ident(dest_schema) || '.' || quote_ident(rn.relname) || ' ADD CONSTRAINT ' || quote_ident(ct.conname) || ' ' || pg_get_constraintdef(ct.oid) || ';' FROM pg_constraint ct JOIN pg_class rn ON rn.oid = ct.conrelid WHERE connamespace = src_oid AND rn.relkind = 'r' AND ct.contype = 'f' LOOP EXECUTE qry; END LOOP; FOR object IN SELECT table_name::text, view_definition FROM information_schema.views WHERE table_schema = quote_ident(source_schema) LOOP buffer := dest_schema || '.' || quote_ident(object); SELECT view_definition INTO v_def FROM information_schema.views WHERE table_schema = quote_ident(source_schema) AND table_name = quote_ident(object); EXECUTE 'CREATE OR REPLACE VIEW ' || buffer || ' AS ' || v_def || ';' ; END LOOP; FOR func_oid IN SELECT oid FROM pg_proc WHERE pronamespace = src_oid LOOP SELECT pg_get_functiondef(func_oid) INTO qry; SELECT replace(qry, source_schema, dest_schema) INTO dest_qry; EXECUTE dest_qry; END LOOP; RETURN; END; $BODY$ LANGUAGE plpgsql VOLATILE COST 100;" - sql_clone = "SELECT clone_schema('#{ref_cloning.source_referential.slug}', '#{ref_cloning.target_referential.slug}_tmp', TRUE);" - sql_drop = "DROP SCHEMA #{ref_cloning.target_referential.slug} CASCADE;" - sql_rename = "ALTER SCHEMA #{ref_cloning.target_referential.slug}_tmp RENAME TO #{ref_cloning.target_referential.slug};" + + source_schema = ref_cloning.source_referential.slug + target_schema = "#{source_schema}_tmp" + + clone_schema ref_cloning, source_schema, target_schema + end + + private + + def clone_schema ref_cloning, source_schema, target_schema ref_cloning.run! - begin - ActiveRecord::Base.connection.execute sql_func - ActiveRecord::Base.connection.execute sql_clone - ActiveRecord::Base.connection.execute sql_drop - ActiveRecord::Base.connection.execute sql_rename - ref_cloning.successful! - rescue Exception => e - Rails.logger.error "ReferentialCloningWorker : #{e}" - ref_cloning.failed! - end + StoredProcedures.invoke_stored_procedure(:clone_schema, source_schema, target_schema, true) + execute_sql "DROP SCHEMA #{source_schema} CASCADE;" + execute_sql "ALTER SCHEMA #{target_schema} RENAME TO #{source_schema};" + + ref_cloning.successful! + rescue Exception => e + Rails.logger.error "ReferentialCloningWorker : #{e}" + ref_cloning.failed! + end + + def execute_sql sql + ActiveRecord::Base.connection.execute sql end end |
