aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlban Peignier2017-12-27 22:47:52 +0100
committerGitHub2017-12-27 22:47:52 +0100
commit5826d342ae08b904ed23c45fd96066a348f04596 (patch)
tree7879c1dd3a496fb90f45efdf0ac79f0881f0c953
parentb754b931eedfdd46a2ce32f1ca19683b6b79931c (diff)
parent9854027e72c5084cc9183b1fc04816d7a914c1e5 (diff)
downloadchouette-core-5826d342ae08b904ed23c45fd96066a348f04596.tar.bz2
Merge pull request #183 from af83/5407-add-purchase-windows-to-vehicle-journeys
Add purchase windows to vehicle journeys. Refs #5407
-rw-r--r--app/assets/stylesheets/typography/_sboiv.sass2
-rw-r--r--app/controllers/autocomplete_purchase_windows_controller.rb12
-rw-r--r--app/controllers/vehicle_journeys_controller.rb1
-rw-r--r--app/javascript/packs/vehicle_journeys/index.js5
-rw-r--r--app/javascript/vehicle_journeys/actions/index.js35
-rw-r--r--app/javascript/vehicle_journeys/components/Filters.js3
-rw-r--r--app/javascript/vehicle_journeys/components/SaveVehicleJourneys.js34
-rw-r--r--app/javascript/vehicle_journeys/components/Tools.js17
-rw-r--r--app/javascript/vehicle_journeys/components/VehicleJourney.js30
-rw-r--r--app/javascript/vehicle_journeys/components/VehicleJourneys.js8
-rw-r--r--app/javascript/vehicle_journeys/components/tools/PurchaseWindowsEditVehicleJourney.js152
-rw-r--r--app/javascript/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js10
-rw-r--r--app/javascript/vehicle_journeys/components/tools/select2s/TimetableSelect2.js14
-rw-r--r--app/javascript/vehicle_journeys/containers/SaveVehicleJourneys.js4
-rw-r--r--app/javascript/vehicle_journeys/containers/tools/PurchaseWindowsEditVehicleJourney.js38
-rw-r--r--app/javascript/vehicle_journeys/reducers/modal.js51
-rw-r--r--app/javascript/vehicle_journeys/reducers/vehicleJourneys.js15
-rw-r--r--app/models/calendar/period.rb2
-rw-r--r--app/models/chouette/purchase_window.rb3
-rw-r--r--app/models/chouette/vehicle_journey.rb4
-rw-r--r--app/models/concerns/period_support.rb4
-rw-r--r--app/views/autocomplete_purchase_windows/index.rabl12
-rw-r--r--app/views/journey_patterns_collections/show.html.slim1
-rw-r--r--app/views/vehicle_journeys/index.html.slim1
-rw-r--r--app/views/vehicle_journeys/show.rabl6
-rw-r--r--config/routes.rb1
-rw-r--r--db/migrate/20171227113809_create_join_table_purchase_windows_vehicle_journeys.rb8
-rw-r--r--db/schema.rb11
-rw-r--r--spec/controllers/autocomplete_purchase_windows_controller_spec.rb33
-rw-r--r--spec/controllers/vehicle_journeys_controller_spec.rb1
-rw-r--r--spec/db/schema_spec.rb2
-rw-r--r--spec/javascript/vehicle_journeys/actions_spec.js91
-rw-r--r--spec/javascript/vehicle_journeys/reducers/modal_spec.js82
-rw-r--r--spec/javascript/vehicle_journeys/reducers/vehicleJourneys_spec.js13
-rw-r--r--spec/models/chouette/vehicle_journey_spec.rb1
35 files changed, 662 insertions, 45 deletions
diff --git a/app/assets/stylesheets/typography/_sboiv.sass b/app/assets/stylesheets/typography/_sboiv.sass
index 0ed2890fa..f0943f843 100644
--- a/app/assets/stylesheets/typography/_sboiv.sass
+++ b/app/assets/stylesheets/typography/_sboiv.sass
@@ -52,6 +52,8 @@
&.sb-5x
font-size: 5em
+ &.sb-strong
+ font-weight: bold
.sb-ZDLR:before
content: '\e904'
diff --git a/app/controllers/autocomplete_purchase_windows_controller.rb b/app/controllers/autocomplete_purchase_windows_controller.rb
new file mode 100644
index 000000000..70dc5a346
--- /dev/null
+++ b/app/controllers/autocomplete_purchase_windows_controller.rb
@@ -0,0 +1,12 @@
+class AutocompletePurchaseWindowsController < ChouetteController
+ respond_to :json, :only => [:index]
+
+ requires_feature :purchase_windows
+
+ include ReferentialSupport
+
+ protected
+ def collection
+ @purchase_windows = referential.purchase_windows.search(params[:q]).result.paginate(page: params[:page])
+ end
+end
diff --git a/app/controllers/vehicle_journeys_controller.rb b/app/controllers/vehicle_journeys_controller.rb
index 454f9c3dd..4bf65a084 100644
--- a/app/controllers/vehicle_journeys_controller.rb
+++ b/app/controllers/vehicle_journeys_controller.rb
@@ -164,6 +164,7 @@ class VehicleJourneysController < ChouetteController
end
def user_permissions
+ @features = Hash[*current_organisation.features.map{|f| [f, true]}.flatten].to_json
policy = policy(:vehicle_journey)
@perms =
%w{create destroy update}.inject({}) do | permissions, action |
diff --git a/app/javascript/packs/vehicle_journeys/index.js b/app/javascript/packs/vehicle_journeys/index.js
index 38431af1d..53c5d5417 100644
--- a/app/javascript/packs/vehicle_journeys/index.js
+++ b/app/javascript/packs/vehicle_journeys/index.js
@@ -23,6 +23,7 @@ var initialState = {
filters: {
selectedJourneyPatterns : selectedJP,
policy: window.perms,
+ features: window.features,
toggleArrivals: false,
queryString: '',
query: {
@@ -54,7 +55,7 @@ var initialState = {
},
status: {
- fetchSuccess: true,
+ fetchSuccess: false,
isFetching: false
},
vehicleJourneys: [],
@@ -99,4 +100,4 @@ render(
<App />
</Provider>,
document.getElementById('vehicle_journeys_wip')
-) \ No newline at end of file
+)
diff --git a/app/javascript/vehicle_journeys/actions/index.js b/app/javascript/vehicle_journeys/actions/index.js
index d4d824cf5..202c09440 100644
--- a/app/javascript/vehicle_journeys/actions/index.js
+++ b/app/javascript/vehicle_journeys/actions/index.js
@@ -100,6 +100,31 @@ const actions = {
vehicleJourneys,
timetables
}),
+ openPurchaseWindowsEditModal : (vehicleJourneys) => ({
+ type : 'EDIT_PURCHASE_WINDOWS_VEHICLEJOURNEY_MODAL',
+ vehicleJourneys
+ }),
+ selectPurchaseWindowsModal: (selectedWindow) =>({
+ type: 'SELECT_PURCHASE_WINDOW_MODAL',
+ selectedItem:{
+ id: selectedWindow.id,
+ name: selectedWindow.name,
+ color: selectedWindow.color,
+ objectid: selectedWindow.objectid
+ }
+ }),
+ addSelectedPurchaseWindow: () => ({
+ type: 'ADD_SELECTED_PURCHASE_WINDOW'
+ }),
+ deletePurchaseWindowsModal : (purchaseWindow) => ({
+ type : 'DELETE_PURCHASE_WINDOW_MODAL',
+ purchaseWindow
+ }),
+ editVehicleJourneyPurchaseWindows : (vehicleJourneys, purchase_windows) => ({
+ type: 'EDIT_VEHICLEJOURNEYS_PURCHASE_WINDOWS',
+ vehicleJourneys,
+ purchase_windows
+ }),
openShiftModal : () => ({
type : 'SHIFT_VEHICLEJOURNEY_MODAL'
}),
@@ -314,6 +339,7 @@ const actions = {
let val
for (val of json.vehicle_journeys){
var timeTables = []
+ var purchaseWindows = []
let tt
for (tt of val.time_tables){
timeTables.push({
@@ -323,6 +349,14 @@ const actions = {
color: tt.color
})
}
+ for (tt of val.purchase_windows){
+ purchaseWindows.push({
+ objectid: tt.objectid,
+ name: tt.name,
+ id: tt.id,
+ color: tt.color
+ })
+ }
let vjasWithDelta = val.vehicle_journey_at_stops.map((vjas, i) => {
actions.fillEmptyFields(vjas)
return actions.getDelta(vjas)
@@ -334,6 +368,7 @@ const actions = {
short_id: val.short_id,
footnotes: val.footnotes,
time_tables: timeTables,
+ purchase_windows: purchaseWindows,
vehicle_journey_at_stops: vjasWithDelta,
deletable: false,
selected: false,
diff --git a/app/javascript/vehicle_journeys/components/Filters.js b/app/javascript/vehicle_journeys/components/Filters.js
index db6707520..3bc4f7ff7 100644
--- a/app/javascript/vehicle_journeys/components/Filters.js
+++ b/app/javascript/vehicle_journeys/components/Filters.js
@@ -33,6 +33,7 @@ export default function Filters({filters, pagination, onFilter, onResetFilters,
onSelect2Timetable={onSelect2Timetable}
hasRoute={true}
chunkURL={("/autocomplete_time_tables.json?route_id=" + String(window.route_id))}
+ searchKey={"comment_or_objectid_cont_any"}
filters={filters}
isFilter={true}
/>
@@ -165,4 +166,4 @@ Filters.propTypes = {
onSelect2Timetable: PropTypes.func.isRequired,
onSelect2JourneyPattern: PropTypes.func.isRequired,
onSelect2VehicleJourney: PropTypes.func.isRequired
-} \ No newline at end of file
+}
diff --git a/app/javascript/vehicle_journeys/components/SaveVehicleJourneys.js b/app/javascript/vehicle_journeys/components/SaveVehicleJourneys.js
index e8c27f92e..8bab5baa9 100644
--- a/app/javascript/vehicle_journeys/components/SaveVehicleJourneys.js
+++ b/app/javascript/vehicle_journeys/components/SaveVehicleJourneys.js
@@ -14,16 +14,27 @@ export default class SaveVehicleJourneys extends Component{
<div className='row mt-md'>
<div className='col-lg-12 text-right'>
<form className='vehicle_journeys formSubmitr ml-xs' onSubmit={e => {e.preventDefault()}}>
- <button
- className='btn btn-default'
- type='button'
- onClick={e => {
- e.preventDefault()
- this.props.editMode ? this.props.onSubmitVehicleJourneys(this.props.dispatch, this.props.vehicleJourneys) : this.props.onEnterEditMode()
- }}
- >
- {this.props.editMode ? "Valider" : "Editer"}
- </button>
+ <div className="btn-group sticky-actions">
+ <button
+ className={'btn ' + (this.props.editMode ? 'btn-success' : 'btn-default') + (this.props.status.fetchSuccess ? '' : ' disabled')}
+ type='button'
+ onClick={e => {
+ e.preventDefault()
+ this.props.editMode ? this.props.onSubmitVehicleJourneys(this.props.dispatch, this.props.vehicleJourneys) : this.props.onEnterEditMode()
+ }}
+ >
+ {this.props.editMode ? "Valider" : "Editer"}
+ </button>
+ {this.props.editMode && <button
+ className='btn btn-default'
+ type='button'
+ onClick={e => {
+ e.preventDefault()
+ this.props.onExitEditMode()
+ }}
+ > Annuler </button>
+ }
+ </div>
</form>
</div>
</div>
@@ -38,5 +49,6 @@ SaveVehicleJourneys.propTypes = {
status: PropTypes.object.isRequired,
filters: PropTypes.object.isRequired,
onEnterEditMode: PropTypes.func.isRequired,
+ onExitEditMode: PropTypes.func.isRequired,
onSubmitVehicleJourneys: PropTypes.func.isRequired
-} \ No newline at end of file
+}
diff --git a/app/javascript/vehicle_journeys/components/Tools.js b/app/javascript/vehicle_journeys/components/Tools.js
index 1ef576529..d6e04f00e 100644
--- a/app/javascript/vehicle_journeys/components/Tools.js
+++ b/app/javascript/vehicle_journeys/components/Tools.js
@@ -7,6 +7,7 @@ import DuplicateVehicleJourney from '../containers/tools/DuplicateVehicleJourney
import EditVehicleJourney from '../containers/tools/EditVehicleJourney'
import NotesEditVehicleJourney from '../containers/tools/NotesEditVehicleJourney'
import TimetablesEditVehicleJourney from '../containers/tools/TimetablesEditVehicleJourney'
+import PurchaseWindowsEditVehicleJourney from '../containers/tools/PurchaseWindowsEditVehicleJourney'
export default class Tools extends Component {
@@ -20,18 +21,26 @@ export default class Tools extends Component {
return this.props.filters.policy[`vehicle_journeys.${key}`]
}
+ hasFeature(key) {
+ // Check if the organisation has the given feature
+ return this.props.filters.features[key]
+ }
+
render() {
let { vehicleJourneys, onCancelSelection, editMode } = this.props
return (
<div className='select_toolbox'>
<ul>
- <AddVehicleJourney disabled={this.hasPolicy("create") && !editMode} />
- <DuplicateVehicleJourney disabled={this.hasPolicy("create") && this.hasPolicy("update") && !editMode}/>
- <ShiftVehicleJourney disabled={this.hasPolicy("update") && !editMode}/>
+ <AddVehicleJourney disabled={!this.hasPolicy("create") || !editMode} />
+ <DuplicateVehicleJourney disabled={!this.hasPolicy("create") || !this.hasPolicy("update") || !editMode}/>
+ <ShiftVehicleJourney disabled={!this.hasPolicy("update") || !editMode}/>
<EditVehicleJourney disabled={!this.hasPolicy("update")}/>
<TimetablesEditVehicleJourney disabled={!this.hasPolicy("update")}/>
+ { this.hasFeature('purchase_windows') &&
+ <PurchaseWindowsEditVehicleJourney disabled={!this.hasPolicy("update")}/>
+ }
<NotesEditVehicleJourney disabled={!this.hasPolicy("update")}/>
- <DeleteVehicleJourneys disabled={this.hasPolicy("destroy") && !editMode}/>
+ <DeleteVehicleJourneys disabled={!this.hasPolicy("destroy") || !editMode}/>
</ul>
<span className='info-msg'>{actions.getSelected(vehicleJourneys).length} course(s) sélectionnée(s)</span>
diff --git a/app/javascript/vehicle_journeys/components/VehicleJourney.js b/app/javascript/vehicle_journeys/components/VehicleJourney.js
index 2ce257284..1e62444ef 100644
--- a/app/javascript/vehicle_journeys/components/VehicleJourney.js
+++ b/app/javascript/vehicle_journeys/components/VehicleJourney.js
@@ -17,6 +17,10 @@ export default class VehicleJourney extends Component {
return bool
}
+ hasFeature(key) {
+ return this.props.filters.features[key]
+ }
+
timeTableURL(tt) {
let refURL = window.location.pathname.split('/', 3).join('/')
let ttURL = refURL + '/time_tables/' + tt.id
@@ -26,6 +30,15 @@ export default class VehicleJourney extends Component {
)
}
+ purchaseWindowURL(tt) {
+ let refURL = window.location.pathname.split('/', 3).join('/')
+ let ttURL = refURL + '/purchase_windows/' + tt.id
+
+ return (
+ <a href={ttURL} title='Voir le calendrier commercial'><span className='fa fa-calendar' style={{color: (tt.color ? tt.color : '')}}></span></a>
+ )
+ }
+
columnHasDelta() {
let a = []
this.props.value.vehicle_journey_at_stops.map((vj, i) => {
@@ -44,11 +57,16 @@ export default class VehicleJourney extends Component {
render() {
this.previousCity = undefined
- let {time_tables} = this.props.value
+ let {time_tables, purchase_windows} = this.props.value
return (
<div className={'t2e-item' + (this.props.value.deletable ? ' disabled' : '') + (this.props.value.errors ? ' has-error': '')}>
- <div className='th'>
+ <div
+ className='th'
+ onClick={(e) =>
+ ($(e.target).parents("a").length == 0) && this.props.editMode && this.props.onSelectVehicleJourney(this.props.index)
+ }
+ >
<div className='strong mb-xs'>{this.props.value.short_id || '-'}</div>
<div>{this.props.value.journey_pattern.short_id || '-'}</div>
<div>
@@ -57,6 +75,14 @@ export default class VehicleJourney extends Component {
)}
{time_tables.length > 3 && <span className='vj_tt'> + {time_tables.length - 3}</span>}
</div>
+ { this.hasFeature('purchase_windows') &&
+ <div>
+ {purchase_windows.slice(0,3).map((tt, i)=>
+ <span key={i} className='vj_tt'>{this.purchaseWindowURL(tt)}</span>
+ )}
+ {purchase_windows.length > 3 && <span className='vj_tt'> + {purchase_windows.length - 3}</span>}
+ </div>
+ }
<div className={(this.props.value.deletable ? 'disabled ' : '') + 'checkbox'}>
<input
id={this.props.index}
diff --git a/app/javascript/vehicle_journeys/components/VehicleJourneys.js b/app/javascript/vehicle_journeys/components/VehicleJourneys.js
index 6bce9766b..e16d03f90 100644
--- a/app/javascript/vehicle_journeys/components/VehicleJourneys.js
+++ b/app/javascript/vehicle_journeys/components/VehicleJourneys.js
@@ -12,6 +12,10 @@ export default class VehicleJourneys extends Component {
this.props.onLoadFirstPage(this.props.filters)
}
+ hasFeature(key) {
+ return this.props.filters.features[key]
+ }
+
componentDidUpdate(prevProps, prevState) {
if(this.props.status.isFetching == false){
$('.table-2entries').each(function() {
@@ -113,6 +117,7 @@ export default class VehicleJourneys extends Component {
<div className='strong mb-xs'>ID course</div>
<div>ID mission</div>
<div>Calendriers</div>
+ { this.hasFeature('purchase_windows') && <div>Calendriers Commerciaux</div> }
</div>
{this.props.stopPointsList.map((sp, i) =>{
return (
@@ -132,6 +137,7 @@ export default class VehicleJourneys extends Component {
index={index}
editMode={this.props.editMode}
filters={this.props.filters}
+ features={this.props.features}
onUpdateTime={this.props.onUpdateTime}
onSelectVehicleJourney={this.props.onSelectVehicleJourney}
/>
@@ -153,4 +159,4 @@ VehicleJourneys.propTypes = {
onLoadFirstPage: PropTypes.func.isRequired,
onUpdateTime: PropTypes.func.isRequired,
onSelectVehicleJourney: PropTypes.func.isRequired
-} \ No newline at end of file
+}
diff --git a/app/javascript/vehicle_journeys/components/tools/PurchaseWindowsEditVehicleJourney.js b/app/javascript/vehicle_journeys/components/tools/PurchaseWindowsEditVehicleJourney.js
new file mode 100644
index 000000000..d61c7a34b
--- /dev/null
+++ b/app/javascript/vehicle_journeys/components/tools/PurchaseWindowsEditVehicleJourney.js
@@ -0,0 +1,152 @@
+import React, { PropTypes, Component } from 'react'
+import actions from '../../actions'
+import TimetableSelect2 from './select2s/TimetableSelect2'
+
+export default class PurchaseWindowsEditVehicleJourney extends Component {
+ constructor(props) {
+ super(props)
+ this.handleSubmit = this.handleSubmit.bind(this)
+ this.purchaseWindowURL = this.purchaseWindowURL.bind(this)
+ }
+
+ handleSubmit() {
+ this.props.onShoppingWindowsEditVehicleJourney(this.props.modal.modalProps.vehicleJourneys, this.props.modal.modalProps.purchase_windows)
+ this.props.onModalClose()
+ $('#PurchaseWindowsEditVehicleJourneyModal').modal('hide')
+ }
+
+ purchaseWindowURL(tt) {
+ let refURL = window.location.pathname.split('/', 3).join('/')
+ return refURL + '/purchase_windows/' + tt.id
+ }
+
+ render() {
+ if(this.props.status.isFetching == true) {
+ return false
+ }
+ if(this.props.status.fetchSuccess == true) {
+ return (
+ <li className='st_action'>
+ <button
+ type='button'
+ disabled={(actions.getSelected(this.props.vehicleJourneys).length < 1 || this.props.disabled)}
+ data-toggle='modal'
+ data-target='#PurchaseWindowsEditVehicleJourneyModal'
+ onClick={() => this.props.onOpenCalendarsEditModal(actions.getSelected(this.props.vehicleJourneys))}
+ title='Calendriers commerciaux'
+ >
+ <span className='sb sb-purchase_window sb-strong'></span>
+ </button>
+
+ <div className={ 'modal fade ' + ((this.props.modal.type == 'duplicate') ? 'in' : '') } id='PurchaseWindowsEditVehicleJourneyModal'>
+ <div className='modal-container'>
+ <div className='modal-dialog'>
+ <div className='modal-content'>
+ <div className='modal-header'>
+ <h4 className='modal-title'>Calendriers commerciaux associés</h4>
+ <span type="button" className="close modal-close" data-dismiss="modal">&times;</span>
+ </div>
+
+ {(this.props.modal.type == 'purchase_windows_edit') && (
+ <form>
+ <div className='modal-body'>
+ <div className='row'>
+ <div className='col-lg-12'>
+ <div className='subform'>
+ <div className='nested-head'>
+ <div className='wrapper'>
+ <div>
+ <div className='form-group'>
+ <label className='control-label'>{this.props.modal.modalProps.purchase_windows.length == 0 ? "Aucun calendrier commercial associé" : "Calendriers commerciaux associés"}</label>
+ </div>
+ </div>
+ <div></div>
+ </div>
+ </div>
+ {this.props.modal.modalProps.purchase_windows.map((tt, i) =>
+ <div className='nested-fields' key={i}>
+ <div className='wrapper'>
+ <div> <a href={this.purchaseWindowURL(tt)} target="_blank">
+ <span className="fa fa-circle mr-xs" style={{color: tt.color}}></span>
+ {tt.name}
+ </a> </div>
+ {
+ this.props.editMode &&
+ <div>
+ <a
+ href='#'
+ title='Supprimer'
+ className='fa fa-trash remove_fields'
+ style={{ height: 'auto', lineHeight: 'normal' }}
+ onClick={(e) => {
+ e.preventDefault()
+ this.props.onDeleteCalendarModal(tt)
+ }}
+ ></a>
+ </div>
+ }
+ </div>
+ </div>
+ )}
+ {
+ this.props.editMode &&
+ <div className='nested-fields'>
+ <div className='wrapper'>
+ <div>
+ <TimetableSelect2
+ onSelect2Timetable={this.props.onSelect2Timetable}
+ chunkURL={'/autocomplete_purchase_windows.json'}
+ searchKey={"name_or_objectid_cont_any"}
+ isFilter={false}
+ />
+ </div>
+ </div>
+ </div>
+ }
+ </div>
+ </div>
+ </div>
+ </div>
+ {
+ this.props.editMode &&
+ <div className='modal-footer'>
+ <button
+ className='btn btn-link'
+ data-dismiss='modal'
+ type='button'
+ onClick={this.props.onModalClose}
+ >
+ Annuler
+ </button>
+ <button
+ className='btn btn-primary'
+ type='button'
+ onClick={this.handleSubmit}
+ >
+ Valider
+ </button>
+ </div>
+ }
+ </form>
+ )}
+
+ </div>
+ </div>
+ </div>
+ </div>
+ </li>
+ )
+ } else {
+ return false
+ }
+ }
+}
+
+PurchaseWindowsEditVehicleJourney.propTypes = {
+ onOpenCalendarsEditModal: PropTypes.func.isRequired,
+ onModalClose: PropTypes.func.isRequired,
+ onShoppingWindowsEditVehicleJourney: PropTypes.func.isRequired,
+ onDeleteCalendarModal: PropTypes.func.isRequired,
+ onSelect2Timetable: PropTypes.func.isRequired,
+ disabled: PropTypes.bool.isRequired
+}
diff --git a/app/javascript/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js b/app/javascript/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js
index 6629135dd..56f80ebb5 100644
--- a/app/javascript/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js
+++ b/app/javascript/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js
@@ -33,6 +33,7 @@ export default class TimetablesEditVehicleJourney extends Component {
data-toggle='modal'
data-target='#CalendarsEditVehicleJourneyModal'
onClick={() => this.props.onOpenCalendarsEditModal(actions.getSelected(this.props.vehicleJourneys))}
+ title='Calendriers'
>
<span className='fa fa-calendar'></span>
</button>
@@ -67,7 +68,7 @@ export default class TimetablesEditVehicleJourney extends Component {
<div className='wrapper'>
<div> <a href={this.timeTableURL(tt)} target="_blank">{tt.comment}</a> </div>
{
- this.props.editMode &&
+ this.props.editMode &&
<div>
<a
href='#'
@@ -85,13 +86,14 @@ export default class TimetablesEditVehicleJourney extends Component {
</div>
)}
{
- this.props.editMode &&
+ this.props.editMode &&
<div className='nested-fields'>
<div className='wrapper'>
<div>
<TimetableSelect2
onSelect2Timetable={this.props.onSelect2Timetable}
chunkURL={'/autocomplete_time_tables.json'}
+ searchKey={"comment_or_objectid_cont_any"}
isFilter={false}
/>
</div>
@@ -103,7 +105,7 @@ export default class TimetablesEditVehicleJourney extends Component {
</div>
</div>
{
- this.props.editMode &&
+ this.props.editMode &&
<div className='modal-footer'>
<button
className='btn btn-link'
@@ -144,4 +146,4 @@ TimetablesEditVehicleJourney.propTypes = {
onDeleteCalendarModal: PropTypes.func.isRequired,
onSelect2Timetable: PropTypes.func.isRequired,
disabled: PropTypes.bool.isRequired
-} \ No newline at end of file
+}
diff --git a/app/javascript/vehicle_journeys/components/tools/select2s/TimetableSelect2.js b/app/javascript/vehicle_journeys/components/tools/select2s/TimetableSelect2.js
index 19c183839..eb8651be2 100644
--- a/app/javascript/vehicle_journeys/components/tools/select2s/TimetableSelect2.js
+++ b/app/javascript/vehicle_journeys/components/tools/select2s/TimetableSelect2.js
@@ -31,12 +31,10 @@ export default class BSelect4 extends Component {
url: origin + path + this.props.chunkURL,
dataType: 'json',
delay: '500',
- data: function(params) {
- return {
- q: {
- comment_or_objectid_cont_any: params.term
- }
- };
+ data: (params) => {
+ let q = {}
+ q[this.props.searchKey] = params.term
+ return {q}
},
processResults: function(data, params) {
return {
@@ -44,7 +42,7 @@ export default class BSelect4 extends Component {
item => _.assign(
{},
item,
- {text: '<strong>' + "<span class='fa fa-circle' style='color:" + (item.color ? item.color : '#4B4B4B') + "'></span> " + item.comment + ' - ' + item.short_id + '</strong><br/><small>' + (item.day_types ? item.day_types.match(/[A-Z]?[a-z]+/g).join(', ') : "") + '</small>'}
+ {text: '<strong>' + "<span class='fa fa-circle' style='color:" + (item.color ? item.color : '#4B4B4B') + "'></span> " + (item.comment || item.name) + ' - ' + item.short_id + '</strong><br/><small>' + (item.day_types ? item.day_types.match(/[A-Z]?[a-z]+/g).join(', ') : "") + '</small>'}
)
)
};
@@ -62,4 +60,4 @@ export default class BSelect4 extends Component {
const formatRepo = (props) => {
if(props.text) return props.text
-} \ No newline at end of file
+}
diff --git a/app/javascript/vehicle_journeys/containers/SaveVehicleJourneys.js b/app/javascript/vehicle_journeys/containers/SaveVehicleJourneys.js
index 18f9e994e..f5f879ed8 100644
--- a/app/javascript/vehicle_journeys/containers/SaveVehicleJourneys.js
+++ b/app/javascript/vehicle_journeys/containers/SaveVehicleJourneys.js
@@ -17,6 +17,10 @@ const mapDispatchToProps = (dispatch) => {
onEnterEditMode: () => {
dispatch(actions.enterEditMode())
},
+ onExitEditMode: () => {
+ dispatch(actions.cancelSelection())
+ dispatch(actions.exitEditMode())
+ },
onSubmitVehicleJourneys: (next, state) => {
actions.submitVehicleJourneys(dispatch, state, next)
}
diff --git a/app/javascript/vehicle_journeys/containers/tools/PurchaseWindowsEditVehicleJourney.js b/app/javascript/vehicle_journeys/containers/tools/PurchaseWindowsEditVehicleJourney.js
new file mode 100644
index 000000000..3fef44489
--- /dev/null
+++ b/app/javascript/vehicle_journeys/containers/tools/PurchaseWindowsEditVehicleJourney.js
@@ -0,0 +1,38 @@
+import actions from '../../actions'
+import { connect } from 'react-redux'
+import PurchaseWindowsEditVehicleJourneyComponent from '../../components/tools/PurchaseWindowsEditVehicleJourney'
+
+const mapStateToProps = (state, ownProps) => {
+ return {
+ editMode: state.editMode,
+ modal: state.modal,
+ vehicleJourneys: state.vehicleJourneys,
+ status: state.status,
+ disabled: ownProps.disabled
+ }
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ onModalClose: () =>{
+ dispatch(actions.closeModal())
+ },
+ onOpenCalendarsEditModal: (vehicleJourneys) =>{
+ dispatch(actions.openPurchaseWindowsEditModal(vehicleJourneys))
+ },
+ onDeleteCalendarModal: (timetable) => {
+ dispatch(actions.deletePurchaseWindowsModal(timetable))
+ },
+ onShoppingWindowsEditVehicleJourney: (vehicleJourneys, timetables) =>{
+ dispatch(actions.editVehicleJourneyPurchaseWindows(vehicleJourneys, timetables))
+ },
+ onSelect2Timetable: (e) =>{
+ dispatch(actions.selectPurchaseWindowsModal(e.params.data))
+ dispatch(actions.addSelectedPurchaseWindow())
+ }
+ }
+}
+
+const PurchaseWindowsEditVehicleJourney = connect(mapStateToProps, mapDispatchToProps)(PurchaseWindowsEditVehicleJourneyComponent)
+
+export default PurchaseWindowsEditVehicleJourney
diff --git a/app/javascript/vehicle_journeys/reducers/modal.js b/app/javascript/vehicle_journeys/reducers/modal.js
index 57f54a144..862e27e1b 100644
--- a/app/javascript/vehicle_journeys/reducers/modal.js
+++ b/app/javascript/vehicle_journeys/reducers/modal.js
@@ -40,7 +40,6 @@ export default function modal(state = {}, action) {
case 'EDIT_CALENDARS_VEHICLEJOURNEY_MODAL':
vehicleJourneysModal = JSON.parse(JSON.stringify(action.vehicleJourneys))
let uniqTimetables = []
- let timetable = {}
vehicleJourneysModal.map((vj, i) => {
vj.time_tables.map((tt, j) =>{
if(!(_.find(uniqTimetables, tt))){
@@ -56,6 +55,24 @@ export default function modal(state = {}, action) {
},
confirmModal: {}
}
+ case 'EDIT_PURCHASE_WINDOWS_VEHICLEJOURNEY_MODAL':
+ var vehicleJourneys = JSON.parse(JSON.stringify(action.vehicleJourneys))
+ let uniqPurchaseWindows = []
+ vehicleJourneys.map((vj, i) => {
+ vj.purchase_windows.map((pw, j) =>{
+ if(!(_.find(uniqPurchaseWindows, pw))){
+ uniqPurchaseWindows.push(pw)
+ }
+ })
+ })
+ return {
+ type: 'purchase_windows_edit',
+ modalProps: {
+ vehicleJourneys: vehicleJourneys,
+ purchase_windows: uniqPurchaseWindows
+ },
+ confirmModal: {}
+ }
case 'SELECT_CP_EDIT_MODAL':
newModalProps = _.assign({}, state.modalProps, {selectedCompany : action.selectedItem})
return _.assign({}, state, {modalProps: newModalProps})
@@ -65,6 +82,9 @@ export default function modal(state = {}, action) {
case 'SELECT_TT_CALENDAR_MODAL':
newModalProps = _.assign({}, state.modalProps, {selectedTimetable : action.selectedItem})
return _.assign({}, state, {modalProps: newModalProps})
+ case 'SELECT_PURCHASE_WINDOW_MODAL':
+ newModalProps = _.assign({}, state.modalProps, {selectedPurchaseWindow : action.selectedItem})
+ return _.assign({}, state, {modalProps: newModalProps})
case 'ADD_SELECTED_TIMETABLE':
if(state.modalProps.selectedTimetable){
newModalProps = JSON.parse(JSON.stringify(state.modalProps))
@@ -73,6 +93,14 @@ export default function modal(state = {}, action) {
}
return _.assign({}, state, {modalProps: newModalProps})
}
+ case 'ADD_SELECTED_PURCHASE_WINDOW':
+ if(state.modalProps.selectedPurchaseWindow){
+ newModalProps = JSON.parse(JSON.stringify(state.modalProps))
+ if (!_.find(newModalProps.purchase_windows, newModalProps.selectedPurchaseWindow)){
+ newModalProps.purchase_windows.push(newModalProps.selectedPurchaseWindow)
+ }
+ return _.assign({}, state, {modalProps: newModalProps})
+ }
case 'DELETE_CALENDAR_MODAL':
newModalProps = JSON.parse(JSON.stringify(state.modalProps))
let timetablesModal = state.modalProps.timetables.slice(0)
@@ -92,6 +120,25 @@ export default function modal(state = {}, action) {
newModalProps.vehicleJourneys = vehicleJourneysModal
newModalProps.timetables = timetablesModal
return _.assign({}, state, {modalProps: newModalProps})
+ case 'DELETE_PURCHASE_WINDOW_MODAL':
+ newModalProps = JSON.parse(JSON.stringify(state.modalProps))
+ let purchase_windows = state.modalProps.purchase_windows.slice(0)
+ purchase_windows.map((tt, i) =>{
+ if(tt == action.purchaseWindow){
+ purchase_windows.splice(i, 1)
+ }
+ })
+ vehicleJourneysModal = state.modalProps.vehicleJourneys.slice(0)
+ vehicleJourneysModal.map((vj) =>{
+ vj.purchase_windows.map((tt, i) =>{
+ if (_.isEqual(tt, action.purchaseWindow)){
+ vj.purchase_windows.splice(i, 1)
+ }
+ })
+ })
+ newModalProps.vehicleJourneys = vehicleJourneysModal
+ newModalProps.purchase_windows = purchase_windows
+ return _.assign({}, state, {modalProps: newModalProps})
case 'CREATE_VEHICLEJOURNEY_MODAL':
let selectedJP = {}
if (window.jpOrigin){
@@ -135,4 +182,4 @@ export default function modal(state = {}, action) {
default:
return state
}
-} \ No newline at end of file
+}
diff --git a/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js b/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js
index 7fed867fa..15d6abe38 100644
--- a/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js
+++ b/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js
@@ -155,6 +155,21 @@ export default function vehicleJourneys(state = [], action) {
return vj
}
})
+ case 'EDIT_VEHICLEJOURNEYS_PURCHASE_WINDOWS':
+ let newWindows = JSON.parse(JSON.stringify(action.purchase_windows))
+ return state.map((vj,i) =>{
+ if(vj.selected){
+ let updatedVJ = _.assign({}, vj)
+ action.vehicleJourneys.map((vjm, j) =>{
+ if(vj.objectid == vjm.objectid){
+ updatedVJ.purchase_windows = newWindows
+ }
+ })
+ return updatedVJ
+ }else{
+ return vj
+ }
+ })
case 'SHIFT_VEHICLEJOURNEY':
return state.map((vj, i) => {
if (vj.selected){
diff --git a/app/models/calendar/period.rb b/app/models/calendar/period.rb
index 1c423dfcc..56ab722fe 100644
--- a/app/models/calendar/period.rb
+++ b/app/models/calendar/period.rb
@@ -1,5 +1,5 @@
class Calendar < ActiveRecord::Base
-
+
class Period
include ActiveAttr::Model
diff --git a/app/models/chouette/purchase_window.rb b/app/models/chouette/purchase_window.rb
index 9f68d4408..e89a0ec7f 100644
--- a/app/models/chouette/purchase_window.rb
+++ b/app/models/chouette/purchase_window.rb
@@ -11,11 +11,12 @@ module Chouette
has_paper_trail
belongs_to :referential
+ has_and_belongs_to_many :vehicle_journeys, :class_name => 'Chouette::VehicleJourney'
validates_presence_of :name, :referential
scope :contains_date, ->(date) { where('date ? <@ any (date_ranges)', date) }
-
+
def self.ransackable_scopes(auth_object = nil)
[:contains_date]
end
diff --git a/app/models/chouette/vehicle_journey.rb b/app/models/chouette/vehicle_journey.rb
index 67bbbe927..983bf5363 100644
--- a/app/models/chouette/vehicle_journey.rb
+++ b/app/models/chouette/vehicle_journey.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
module Chouette
class VehicleJourney < Chouette::TridentActiveRecord
has_paper_trail
@@ -23,6 +24,7 @@ module Chouette
belongs_to :journey_pattern
has_and_belongs_to_many :footnotes, :class_name => 'Chouette::Footnote'
+ has_and_belongs_to_many :purchase_windows, :class_name => 'Chouette::PurchaseWindow'
validates_presence_of :route
validates_presence_of :journey_pattern
@@ -139,7 +141,7 @@ module Chouette
end
def update_has_and_belongs_to_many_from_state item
- ['time_tables', 'footnotes'].each do |assos|
+ ['time_tables', 'footnotes', 'purchase_windows'].each do |assos|
saved = self.send(assos).map(&:id)
(saved - item[assos].map{|t| t['id']}).each do |id|
diff --git a/app/models/concerns/period_support.rb b/app/models/concerns/period_support.rb
index f512c4e89..e17451fe4 100644
--- a/app/models/concerns/period_support.rb
+++ b/app/models/concerns/period_support.rb
@@ -7,7 +7,7 @@ module PeriodSupport
def init_date_ranges
self.date_ranges ||= []
end
-
+
### Calendar::Period
# Required by coocon
def build_period
@@ -77,4 +77,4 @@ module PeriodSupport
private :clear_periods
end
-end \ No newline at end of file
+end
diff --git a/app/views/autocomplete_purchase_windows/index.rabl b/app/views/autocomplete_purchase_windows/index.rabl
new file mode 100644
index 000000000..1d0287602
--- /dev/null
+++ b/app/views/autocomplete_purchase_windows/index.rabl
@@ -0,0 +1,12 @@
+collection @purchase_windows, :object_root => false
+
+node do |window|
+ {
+ :id => window.id,
+ :name => window.name,
+ :objectid => window.objectid,
+ :color => window.color,
+ :short_id => window.get_objectid.short_id,
+ :text => "<strong><span class='fa fa-circle' style='color:" + (window.color ? window.color : '#4b4b4b') + "'></span> " + window.name + " - " + window.get_objectid.short_id + "</strong>"
+ }
+end
diff --git a/app/views/journey_patterns_collections/show.html.slim b/app/views/journey_patterns_collections/show.html.slim
index 834501da3..97a62f7db 100644
--- a/app/views/journey_patterns_collections/show.html.slim
+++ b/app/views/journey_patterns_collections/show.html.slim
@@ -18,5 +18,6 @@
| window.journeyPatternLength = #{@journey_patterns.total_entries()};
| window.journeyPatternsPerPage = #{@ppage};
| window.perms = #{raw @perms}
+ | window.features = #{raw @features};
= javascript_pack_tag 'journey_patterns/index.js'
diff --git a/app/views/vehicle_journeys/index.html.slim b/app/views/vehicle_journeys/index.html.slim
index 4ad9d524d..595646808 100644
--- a/app/views/vehicle_journeys/index.html.slim
+++ b/app/views/vehicle_journeys/index.html.slim
@@ -25,6 +25,7 @@
| window.vehicleJourneysPerPage = #{@ppage};
| window.line_footnotes = #{raw @footnotes};
| window.perms = #{raw @perms};
+ | window.features = #{raw @features};
| window.I18n = #{(I18n.backend.send(:translations).to_json).html_safe};
= javascript_pack_tag 'vehicle_journeys/index.js'
diff --git a/app/views/vehicle_journeys/show.rabl b/app/views/vehicle_journeys/show.rabl
index a39a53b05..b35ae65f9 100644
--- a/app/views/vehicle_journeys/show.rabl
+++ b/app/views/vehicle_journeys/show.rabl
@@ -28,6 +28,12 @@ child(:time_tables, :object_root => false) do |time_tables|
end
end
+if has_feature? :purchase_windows
+ child(:purchase_windows, :object_root => false) do |purchase_windows|
+ attributes :id, :objectid, :name, :color
+ end
+end
+
child :footnotes, :object_root => false do |footnotes|
attributes :id, :code, :label
end
diff --git a/config/routes.rb b/config/routes.rb
index e05f5d365..22ff58724 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -112,6 +112,7 @@ ChouetteIhm::Application.routes.draw do
resources :autocomplete_stop_areas, only: [:show, :index] do
get 'around', on: :member
end
+ resources :autocomplete_purchase_windows, only: [:index]
get :select_compliance_control_set
post :validate, on: :member
resources :autocomplete_time_tables, only: [:index]
diff --git a/db/migrate/20171227113809_create_join_table_purchase_windows_vehicle_journeys.rb b/db/migrate/20171227113809_create_join_table_purchase_windows_vehicle_journeys.rb
new file mode 100644
index 000000000..5460878aa
--- /dev/null
+++ b/db/migrate/20171227113809_create_join_table_purchase_windows_vehicle_journeys.rb
@@ -0,0 +1,8 @@
+class CreateJoinTablePurchaseWindowsVehicleJourneys < ActiveRecord::Migration
+ def change
+ create_join_table :purchase_windows, :vehicle_journeys do |t|
+ t.integer :purchase_window_id, limit: 8
+ t.integer :vehicle_journey_id, limit: 8
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 182df3159..67c42f568 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,12 +11,12 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20171220164059) do
+ActiveRecord::Schema.define(version: 20171227113809) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
- enable_extension "postgis"
enable_extension "hstore"
+ enable_extension "postgis"
enable_extension "unaccent"
create_table "access_links", id: :bigserial, force: :cascade do |t|
@@ -403,9 +403,9 @@ ActiveRecord::Schema.define(version: 20171220164059) do
t.string "type"
t.integer "parent_id", limit: 8
t.string "parent_type"
+ t.datetime "notified_parent_at"
t.integer "current_step", default: 0
t.integer "total_steps", default: 0
- t.datetime "notified_parent_at"
t.string "creator"
end
@@ -586,6 +586,11 @@ ActiveRecord::Schema.define(version: 20171220164059) do
add_index "purchase_windows", ["referential_id"], name: "index_purchase_windows_on_referential_id", using: :btree
+ create_table "purchase_windows_vehicle_journeys", id: false, force: :cascade do |t|
+ t.integer "purchase_window_id", limit: 8
+ t.integer "vehicle_journey_id", limit: 8
+ end
+
create_table "referential_clonings", id: :bigserial, force: :cascade do |t|
t.string "status"
t.datetime "started_at"
diff --git a/spec/controllers/autocomplete_purchase_windows_controller_spec.rb b/spec/controllers/autocomplete_purchase_windows_controller_spec.rb
new file mode 100644
index 000000000..cea600ea8
--- /dev/null
+++ b/spec/controllers/autocomplete_purchase_windows_controller_spec.rb
@@ -0,0 +1,33 @@
+require 'rails_helper'
+
+RSpec.describe AutocompletePurchaseWindowsController, type: :controller do
+ login_user
+
+ let(:referential) { Referential.first }
+ let!(:window) { create :purchase_window, referential: referential, name: 'écolà militaire' }
+
+ describe 'GET #index' do
+ it 'should be unauthorized' do
+ expect { get(:index, referential_id: referential.id) }.to raise_error(FeatureChecker::NotAuthorizedError)
+ end
+
+ with_feature "purchase_windows" do
+ let(:request){ get(:index, referential_id: referential.id) }
+ before do
+ request
+ end
+
+ it 'should be successful' do
+ expect(response).to be_success
+ end
+
+ context 'search by name' do
+ let(:request){ get :index, referential_id: referential.id, q: {name_or_objectid_cont_any: 'écolà'}, :format => :json }
+ it 'should be successful' do
+ expect(response).to be_success
+ expect(assigns(:purchase_windows)).to eq([window])
+ end
+ end
+ end
+ end
+end
diff --git a/spec/controllers/vehicle_journeys_controller_spec.rb b/spec/controllers/vehicle_journeys_controller_spec.rb
index c9356fffa..416450c21 100644
--- a/spec/controllers/vehicle_journeys_controller_spec.rb
+++ b/spec/controllers/vehicle_journeys_controller_spec.rb
@@ -10,6 +10,7 @@ RSpec.describe VehicleJourneysController, :type => :controller do
before do
allow(controller).to receive(:pundit_user).and_return(user_context)
+ allow(controller).to receive(:current_organisation).and_return(@user.organisation)
end
it 'computes them correctly if not authorized' do
diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb
index 585636124..a7fe0a162 100644
--- a/spec/db/schema_spec.rb
+++ b/spec/db/schema_spec.rb
@@ -44,6 +44,6 @@ Diff: #{diff}
def diff
RSpec::Support::Differ.new(
color: RSpec::Matchers.configuration.color?
- ).diff_as_string(@expected, @original)
+ ).diff_as_string(@original, @expected)
end
end
diff --git a/spec/javascript/vehicle_journeys/actions_spec.js b/spec/javascript/vehicle_journeys/actions_spec.js
index 789507482..3af19ebc3 100644
--- a/spec/javascript/vehicle_journeys/actions_spec.js
+++ b/spec/javascript/vehicle_journeys/actions_spec.js
@@ -209,6 +209,13 @@ describe('when clicking on edit notes modal', () => {
expect(actions.openNotesEditModal(vehicleJourney)).toEqual(expectedAction)
})
})
+
+ // ___ ___ ___ _____ _ _ ___ _____ ___ ___
+ // | __/ _ \ / _ \_ _| \| |/ _ \_ _| __/ __|
+ // | _| (_) | (_) || | | .` | (_) || | | _|\__ \
+ // |_| \___/ \___/ |_| |_|\_|\___/ |_| |___|___/
+ //
+
describe('when clicking on a footnote button inside footnote modal', () => {
it('should create an action to toggle this footnote', () => {
const footnote = {}, isShown = true
@@ -230,6 +237,13 @@ describe('when clicking on validate button inside footnote modal', () => {
expect(actions.editVehicleJourneyNotes(footnotes)).toEqual(expectedAction)
})
})
+
+ // _____ ___ __ __ ___ _____ _ ___ _ ___ ___
+ // |_ _|_ _| \/ | __|_ _/_\ | _ ) | | __/ __|
+ // | | | || |\/| | _| | |/ _ \| _ \ |__| _|\__ \
+ // |_| |___|_| |_|___| |_/_/ \_\___/____|___|___/
+ //
+
describe('when clicking on calendar button in toolbox', () => {
it('should create an action to open calendar modal', () => {
const vehicleJourneys = []
@@ -288,6 +302,83 @@ describe('when using select2 to pick a timetable', () => {
expect(actions.selectTTCalendarsModal(selectedTT)).toEqual(expectedAction)
})
})
+
+ // ___ _ _ ___ ___ _ _ _ ___ ___
+ // | _ \ | | | _ \/ __| || | /_\ / __| __|
+ // | _/ |_| | / (__| __ |/ _ \\__ \ _|
+ // |_| \___/|_|_\\___|_||_/_/_\_\___/___|__
+ // \ \ / /_ _| \| | \ / _ \ \ / / __|
+ // \ \/\/ / | || .` | |) | (_) \ \/\/ /\__ \
+ // \_/\_/ |___|_|\_|___/ \___/ \_/\_/ |___/
+ //
+
+describe('when clicking on purchase window button in toolbox', () => {
+ it('should create an action to open purchase window modal', () => {
+ const vehicleJourneys = []
+ const expectedAction = {
+ type: 'EDIT_PURCHASE_WINDOWS_VEHICLEJOURNEY_MODAL',
+ vehicleJourneys
+ }
+ expect(actions.openPurchaseWindowsEditModal(vehicleJourneys)).toEqual(expectedAction)
+ })
+})
+describe('when clicking on delete button next to a purchase window inside modal', () => {
+ it('should create an action to delete purchase window from selected vehicle journeys', () => {
+ const purchaseWindow = {}
+ const expectedAction = {
+ type: 'DELETE_PURCHASE_WINDOW_MODAL',
+ purchaseWindow
+ }
+ expect(actions.deletePurchaseWindowsModal(purchaseWindow)).toEqual(expectedAction)
+ })
+})
+describe('when clicking on validate button inside purchase windows modal', () => {
+ it('should create an action to update vj purchase windows', () => {
+ const vehicleJourneys = []
+ const purchase_windows = []
+ const expectedAction = {
+ type: 'EDIT_VEHICLEJOURNEYS_PURCHASE_WINDOWS',
+ vehicleJourneys,
+ purchase_windows
+ }
+ expect(actions.editVehicleJourneyPurchaseWindows(vehicleJourneys, purchase_windows)).toEqual(expectedAction)
+ })
+})
+describe('when clicking on add button inside purchase windows modal', () => {
+ it('should create an action to add the selected purchase window to preselected vjs', () => {
+ const expectedAction = {
+ type: 'ADD_SELECTED_PURCHASE_WINDOW',
+ }
+ expect(actions.addSelectedPurchaseWindow()).toEqual(expectedAction)
+ })
+})
+describe('when using select2 to pick a purchase window', () => {
+ it('should create an action to select a purchase window inside modal', () => {
+ let selectedTT = {
+ id: 1,
+ objectid: 2,
+ name: 'test',
+ color: 'color',
+ }
+ const expectedAction = {
+ type: 'SELECT_PURCHASE_WINDOW_MODAL',
+ selectedItem:{
+ id: selectedTT.id,
+ objectid: selectedTT.objectid,
+ name: selectedTT.name,
+ color: "color"
+ }
+ }
+ expect(actions.selectPurchaseWindowsModal(selectedTT)).toEqual(expectedAction)
+ })
+})
+
+ // ___ ___ _ _____ ___ ___ ___
+ // | __|_ _| ||_ _| __| _ \/ __|
+ // | _| | || |__| | | _|| /\__ \
+ // |_| |___|____|_| |___|_|_\|___/
+ //
+
describe('when clicking on reset button inside query filters', () => {
it('should create an action to reset the query filters', () => {
const expectedAction = {
diff --git a/spec/javascript/vehicle_journeys/reducers/modal_spec.js b/spec/javascript/vehicle_journeys/reducers/modal_spec.js
index 69de9168b..ea8a002d2 100644
--- a/spec/javascript/vehicle_journeys/reducers/modal_spec.js
+++ b/spec/javascript/vehicle_journeys/reducers/modal_spec.js
@@ -91,6 +91,12 @@ describe('modal reducer', () => {
).toEqual(newState)
})
+ // _____ ___ __ __ ___ _____ _ ___ _ ___ ___
+ // |_ _|_ _| \/ | __|_ _/_\ | _ ) | | __/ __|
+ // | | | || |\/| | _| | |/ _ \| _ \ |__| _|\__ \
+ // |_| |___|_| |_|___| |_/_/ \_\___/____|___|___/
+ //
+
it('should handle EDIT_CALENDARS_VEHICLEJOURNEY_MODAL', () => {
let vehicleJourneys = []
let modalPropsResult = {
@@ -158,6 +164,82 @@ describe('modal reducer', () => {
).toEqual(newState)
})
+ // ___ _ _ ___ ___ _ _ _ ___ ___
+ // | _ \ | | | _ \/ __| || | /_\ / __| __|
+ // | _/ |_| | / (__| __ |/ _ \\__ \ _|
+ // |_| \___/|_|_\\___|_||_/_/_\_\___/___|__
+ // \ \ / /_ _| \| | \ / _ \ \ / / __|
+ // \ \/\/ / | || .` | |) | (_) \ \/\/ /\__ \
+ // \_/\_/ |___|_|\_|___/ \___/ \_/\_/ |___/
+ //
+
+ it('should handle EDIT_PURCHASE_WINDOWS_VEHICLEJOURNEY_MODAL', () => {
+ let vehicleJourneys = []
+ let modalPropsResult = {
+ vehicleJourneys: [],
+ purchase_windows: []
+ }
+ expect(
+ modalReducer(state, {
+ type: 'EDIT_PURCHASE_WINDOWS_VEHICLEJOURNEY_MODAL',
+ vehicleJourneys
+ })
+ ).toEqual(Object.assign({}, state, {type: 'purchase_windows_edit', modalProps: modalPropsResult}))
+ })
+
+ it('should handle SELECT_PURCHASE_WINDOW_MODAL', () => {
+ let newModalProps = {selectedPurchaseWindow : {id: 1}}
+ expect(
+ modalReducer(state, {
+ type: 'SELECT_PURCHASE_WINDOW_MODAL',
+ selectedItem: {id: 1}
+ })
+ ).toEqual(Object.assign({}, state, {modalProps: newModalProps}))
+ })
+
+ it('should handle ADD_SELECTED_PURCHASE_WINDOW', () => {
+ let fakeWindows = [{'test': 'test'}, {'test 2': 'test 2'}]
+ let newWindows = [{'test': 'test'}, {'test 2': 'test 2'}, {'add': 'add'}]
+ let fakeVehicleJourneys= [{purchase_windows: fakeWindows}, {purchase_windows: newWindows}]
+ state.modalProps.vehicleJourneys = fakeVehicleJourneys
+ state.modalProps.purchase_windows = fakeWindows
+ state.modalProps.selectedPurchaseWindow = {'add': 'add'}
+ let newState = {
+ type: '',
+ modalProps:{
+ vehicleJourneys: fakeVehicleJourneys,
+ purchase_windows: [{'test': 'test'},{'test 2': 'test 2'},{'add': 'add'}],
+ selectedPurchaseWindow: {'add': 'add'}
+ },
+ confirmModal: {}
+ }
+ expect(
+ modalReducer(state, {
+ type: 'ADD_SELECTED_PURCHASE_WINDOW',
+ })
+ ).toEqual(newState)
+ })
+
+ it('should handle DELETE_PURCHASE_WINDOW_MODAL', () => {
+ let deletableWindow = {'delete': 'delete'}
+ let fakeWindows = [{'test': 'test'}, {'test 2': 'test 2'}, deletableWindow]
+ let newWindows = [{'test': 'test'}, {'test 2': 'test 2'}]
+ let fakeVehicleJourneys= [{purchase_windows: fakeWindows}, {purchase_windows: newWindows}]
+ state.modalProps = Object.assign({}, state.modalProps,{vehicleJourneys : fakeVehicleJourneys, purchase_windows: fakeWindows })
+ let newState = {
+ // for the sake of the test, no need to specify the type
+ type: '',
+ modalProps:{vehicleJourneys: [{purchase_windows: newWindows},{purchase_windows: newWindows}], purchase_windows: newWindows},
+ confirmModal: {}
+ }
+ expect(
+ modalReducer(state, {
+ type: 'DELETE_PURCHASE_WINDOW_MODAL',
+ purchaseWindow: deletableWindow
+ })
+ ).toEqual(newState)
+ })
+
it('should handle SELECT_CP_EDIT_MODAL', () => {
let newModalProps = {selectedCompany : {name: 'ALBATRANS'}}
expect(
diff --git a/spec/javascript/vehicle_journeys/reducers/vehicleJourneys_spec.js b/spec/javascript/vehicle_journeys/reducers/vehicleJourneys_spec.js
index 1c2cc1577..c834de1f6 100644
--- a/spec/javascript/vehicle_journeys/reducers/vehicleJourneys_spec.js
+++ b/spec/javascript/vehicle_journeys/reducers/vehicleJourneys_spec.js
@@ -254,7 +254,6 @@ describe('vehicleJourneys reducer', () => {
).toEqual([newVJ, state[1]])
})
-
it('should handle EDIT_VEHICLEJOURNEYS_TIMETABLES', () => {
let newState = JSON.parse(JSON.stringify(state))
newState[0].time_tables = [fakeTimeTables[0]]
@@ -266,4 +265,16 @@ describe('vehicleJourneys reducer', () => {
})
).toEqual(newState)
})
+
+ it('should handle EDIT_VEHICLEJOURNEYS_PURCHASE_WINDOWS', () => {
+ let newState = JSON.parse(JSON.stringify(state))
+ newState[0].purchase_windows = [fakeTimeTables[0]]
+ expect(
+ vjReducer(state, {
+ type: 'EDIT_VEHICLEJOURNEYS_PURCHASE_WINDOWS',
+ vehicleJourneys: state,
+ purchase_windows: [fakeTimeTables[0]]
+ })
+ ).toEqual(newState)
+ })
})
diff --git a/spec/models/chouette/vehicle_journey_spec.rb b/spec/models/chouette/vehicle_journey_spec.rb
index ac9b21ceb..60ab2b818 100644
--- a/spec/models/chouette/vehicle_journey_spec.rb
+++ b/spec/models/chouette/vehicle_journey_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
describe Chouette::VehicleJourney, :type => :model do
it { is_expected.to be_versioned }
+ it { should have_and_belong_to_many(:purchase_windows) }
it "must be valid with an at-stop day offset of 1" do
vehicle_journey = create(