diff options
| author | Luc Donnet | 2018-01-11 22:19:50 +0100 | 
|---|---|---|
| committer | GitHub | 2018-01-11 22:19:50 +0100 | 
| commit | 266563da6b501c319156c06628b8a3702fd84fd4 (patch) | |
| tree | f5e54f4d579da66c26328945019a8e263a835ef7 | |
| parent | d01f7668bf8dca08fc34c7e27e87e6284070fbd7 (diff) | |
| parent | dab42556bd956aa07fa2e423e85a647c166b9e0e (diff) | |
| download | chouette-core-266563da6b501c319156c06628b8a3702fd84fd4.tar.bz2 | |
Merge pull request #227 from af83/5551-handle-custom-fields-in-vjs-editor
5551 Handle custom fields in VJs editor
19 files changed, 193 insertions, 16 deletions
| diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 474277da1..80d194096 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -41,6 +41,11 @@ class ApplicationController < ActionController::Base    end    helper_method :current_offer_workbench +  def current_workgroup +    current_offer_workbench.workgroup +  end +  helper_method :current_workgroup +    def current_functional_scope      functional_scope = current_organisation.sso_attributes.try(:[], "functional_scope") if current_organisation      JSON.parse(functional_scope) if functional_scope diff --git a/app/controllers/vehicle_journeys_controller.rb b/app/controllers/vehicle_journeys_controller.rb index c5466abe5..eb3367d0c 100644 --- a/app/controllers/vehicle_journeys_controller.rb +++ b/app/controllers/vehicle_journeys_controller.rb @@ -49,6 +49,7 @@ class VehicleJourneysController < ChouetteController        end        format.html do          load_missions +        load_custom_fields          @stop_points_list = []          @stop_points_list = route.stop_points.includes(:stop_area).map do |sp|            { @@ -177,6 +178,10 @@ class VehicleJourneysController < ChouetteController    end    private +  def load_custom_fields +    @custom_fields = current_workgroup.custom_fields_definitions +  end +    def load_missions      @all_missions = route.journey_patterns.count > 10 ? [] : route.journey_patterns.map do |item|        { diff --git a/app/javascript/packs/vehicle_journeys/index.js b/app/javascript/packs/vehicle_journeys/index.js index ab28371fe..aa5738d59 100644 --- a/app/javascript/packs/vehicle_journeys/index.js +++ b/app/javascript/packs/vehicle_journeys/index.js @@ -71,7 +71,8 @@ var initialState = {      modalProps: {},      confirmModal: {}    }, -  missions: window.all_missions +  missions: window.all_missions, +  custom_fields: window.custom_fields  }  if (window.jpOrigin){ diff --git a/app/javascript/vehicle_journeys/components/tools/CreateModal.js b/app/javascript/vehicle_journeys/components/tools/CreateModal.js index 07c684760..90328458b 100644 --- a/app/javascript/vehicle_journeys/components/tools/CreateModal.js +++ b/app/javascript/vehicle_journeys/components/tools/CreateModal.js @@ -3,15 +3,17 @@ import PropTypes from 'prop-types'  import actions from '../../actions'  import MissionSelect2 from './select2s/MissionSelect2'  import CompanySelect2 from './select2s/CompanySelect2' +import CustomFieldsInputs from './CustomFieldsInputs'  export default class CreateModal extends Component {    constructor(props) {      super(props) +    this.custom_fields = _.assign({}, this.props.custom_fields)    }    handleSubmit() {      if (actions.validateFields(...this.refs, $('.vjCreateSelectJP')[0]) && this.props.modal.modalProps.selectedJPModal) { -      this.props.onAddVehicleJourney(this.refs, this.props.modal.modalProps.selectedJPModal, this.props.stopPointsList, this.props.modal.modalProps.vehicleJourney && this.props.modal.modalProps.vehicleJourney.company) +      this.props.onAddVehicleJourney(_.assign({}, this.refs, {custom_fields: this.custom_fields}), this.props.modal.modalProps.selectedJPModal, this.props.stopPointsList, this.props.modal.modalProps.vehicleJourney && this.props.modal.modalProps.vehicleJourney.company)        this.props.onModalClose()        $('#NewVehicleJourneyModal').modal('hide')      } @@ -89,6 +91,11 @@ export default class CreateModal extends Component {                                  />                              </div>                            </div> +                          <CustomFieldsInputs +                            values={this.props.custom_fields} +                            onUpdate={(code, value) => this.custom_fields[code]["value"] = value} +                            disabled={false} +                          />                            { this.props.modal.modalProps.selectedJPModal && this.props.modal.modalProps.selectedJPModal.full_schedule && <div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'>                                <div className='form-group'>                                  <label className='control-label'>Heure de départ</label> diff --git a/app/javascript/vehicle_journeys/components/tools/CustomFieldsInputs.js b/app/javascript/vehicle_journeys/components/tools/CustomFieldsInputs.js new file mode 100644 index 000000000..90d72a801 --- /dev/null +++ b/app/javascript/vehicle_journeys/components/tools/CustomFieldsInputs.js @@ -0,0 +1,50 @@ +import _ from 'lodash' +import Select2 from 'react-select2-wrapper' +import React, { Component } from 'react' +import PropTypes from 'prop-types' + +export default class CustomFieldsInputs extends Component { +  constructor(props) { +    super(props) +  } + +  listInput(cf){ +    return( +      <Select2 +        data={_.map(cf.options.list_values, (v, k) => { +          return {id: k, text: (v.length > 0 ? v : '\u00A0')} +        })} +        ref={'custom_fields.' + cf.code} +        className='form-control' +        defaultValue={cf.value} +        disabled={this.props.disabled} +        options={{ +          theme: 'bootstrap', +          width: '100%' +        }} +        onSelect={(e) => this.props.onUpdate(cf.code, e.params.data.id) } +      /> +    ) +  } + +  render() { +    return ( +      <div> +        {_.map(this.props.values, (cf, code) => +          <div className='col-lg-6 col-md-6 col-sm-6 col-xs-12' key={code}> +            <div className='form-group'> +              <label className='control-label'>{cf.name}</label> +              {this[cf.field_type + "Input"](cf)} +            </div> +          </div> +        )} +      </div> +    ) +  } +} + +CustomFieldsInputs.propTypes = { +  onUpdate: PropTypes.func.isRequired, +  values: PropTypes.object.isRequired, +  disabled: PropTypes.bool.isRequired +} diff --git a/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js b/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js index 08d74baba..2893422f8 100644 --- a/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js +++ b/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js @@ -2,6 +2,7 @@ import React, { Component } from 'react'  import PropTypes from 'prop-types'  import actions from '../../actions'  import CompanySelect2 from './select2s/CompanySelect2' +import CustomFieldsInputs from './CustomFieldsInputs'  export default class EditVehicleJourney extends Component {    constructor(props) { @@ -15,8 +16,8 @@ export default class EditVehicleJourney extends Component {          company = this.props.modal.modalProps.selectedCompany        } else if (typeof this.props.modal.modalProps.vehicleJourney.company === "object") {          company = this.props.modal.modalProps.vehicleJourney.company -      }  -      this.props.onEditVehicleJourney(this.refs, company) +      } +      this.props.onEditVehicleJourney(_.assign({}, this.refs, {custom_fields: this.custom_fields}), company)        this.props.onModalClose()        $('#EditVehicleJourneyModal').modal('hide')      } @@ -27,6 +28,9 @@ export default class EditVehicleJourney extends Component {        return false      }      if(this.props.status.fetchSuccess == true) { +      if(this.props.modal.modalProps.vehicleJourney){ +        this.custom_fields = _.assign({}, this.props.modal.modalProps.vehicleJourney.custom_fields) +      }        return (          <li className='st_action'>            <button @@ -140,8 +144,15 @@ export default class EditVehicleJourney extends Component {                              defaultValue={this.props.modal.modalProps.vehicleJourney.checksum}                              />                          </div> - +                        <div className='row'> +                          <CustomFieldsInputs +                            values={this.props.modal.modalProps.vehicleJourney.custom_fields} +                            onUpdate={(code, value) => this.custom_fields[code]["value"] = value} +                            disabled={!this.props.editMode} +                          /> +                        </div>                        </div> +                        {                          this.props.editMode &&                          <div className='modal-footer'> diff --git a/app/javascript/vehicle_journeys/containers/tools/AddVehicleJourney.js b/app/javascript/vehicle_journeys/containers/tools/AddVehicleJourney.js index 0f4a0ea7d..0db7628be 100644 --- a/app/javascript/vehicle_journeys/containers/tools/AddVehicleJourney.js +++ b/app/javascript/vehicle_journeys/containers/tools/AddVehicleJourney.js @@ -10,6 +10,7 @@ const mapStateToProps = (state, ownProps) => {      status: state.status,      stopPointsList: state.stopPointsList,      missions: state.missions, +    custom_fields: state.custom_fields,    }  } diff --git a/app/javascript/vehicle_journeys/reducers/custom_fields.js b/app/javascript/vehicle_journeys/reducers/custom_fields.js new file mode 100644 index 000000000..482fd91cb --- /dev/null +++ b/app/javascript/vehicle_journeys/reducers/custom_fields.js @@ -0,0 +1,6 @@ +export default function custom_fields(state = [], action) { +  switch (action.type) { +    default: +      return state +  } +} diff --git a/app/javascript/vehicle_journeys/reducers/index.js b/app/javascript/vehicle_journeys/reducers/index.js index 862c864ae..1963f7c6d 100644 --- a/app/javascript/vehicle_journeys/reducers/index.js +++ b/app/javascript/vehicle_journeys/reducers/index.js @@ -7,6 +7,7 @@ import filters from './filters'  import editMode from './editMode'  import stopPointsList from './stopPointsList'  import missions from './missions' +import custom_fields from './custom_fields'  const vehicleJourneysApp = combineReducers({    vehicleJourneys, @@ -16,7 +17,8 @@ const vehicleJourneysApp = combineReducers({    filters,    editMode,    stopPointsList, -  missions +  missions, +  custom_fields  })  export default vehicleJourneysApp diff --git a/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js b/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js index 0549c7adc..62b846d9a 100644 --- a/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js +++ b/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js @@ -54,6 +54,7 @@ const vehicleJourney= (state = {}, action, keep) => {          pristineVjasList.push(newVjas)        }) +        return {          company: action.selectedCompany,          journey_pattern: action.selectedJourneyPattern, @@ -68,7 +69,8 @@ const vehicleJourney= (state = {}, action, keep) => {          selected: false,          deletable: false,          transport_mode: window.transportMode ? window.transportMode : 'undefined', -        transport_submode: window.transportSubmode ? window.transportSubmode : 'undefined' +        transport_submode: window.transportSubmode ? window.transportSubmode : 'undefined', +        custom_fields: action.data.custom_fields        }      case 'DUPLICATE_VEHICLEJOURNEY':      case 'SHIFT_VEHICLEJOURNEY': @@ -148,6 +150,7 @@ export default function vehicleJourneys(state = [], action) {              company: action.selectedCompany,              published_journey_name: action.data.published_journey_name.value,              published_journey_identifier: action.data.published_journey_identifier.value, +            custom_fields: action.data.custom_fields,            })          }else{            return vj diff --git a/app/models/chouette/vehicle_journey.rb b/app/models/chouette/vehicle_journey.rb index 67216e422..8a704d8c0 100644 --- a/app/models/chouette/vehicle_journey.rb +++ b/app/models/chouette/vehicle_journey.rb @@ -224,6 +224,7 @@ module Chouette        ['company', 'journey_pattern'].map do |association|          attrs["#{association}_id"] = item[association]['id'] if item[association]        end +      attrs["custom_field_values"] = Hash[*(item["custom_fields"] || {}).map{|k, v| [k, v["value"]]}.flatten]        attrs      end @@ -262,8 +263,15 @@ module Chouette        end      end +    def self.custom_fields +      CustomField.where(resource_type: self.name.split("::").last) +    end + +      def custom_fields -      CustomField.where(resource_type: self.class.name.split("::").last) +      Hash[*self.class.custom_fields.map do |v| +        [v.code, v.slice(:code, :name, :field_type, :options).update(value: custom_field_value(v.code))] +      end.flatten]      end      def custom_field_value key diff --git a/app/models/workgroup.rb b/app/models/workgroup.rb index 995917fac..511bbfeb0 100644 --- a/app/models/workgroup.rb +++ b/app/models/workgroup.rb @@ -11,4 +11,8 @@ class Workgroup < ActiveRecord::Base    validates_presence_of :stop_area_referential_id    has_many :custom_fields + +  def custom_fields_definitions +    Hash[*custom_fields.map{|cf| [cf.code, cf]}.flatten] +  end  end diff --git a/app/views/vehicle_journeys/index.html.slim b/app/views/vehicle_journeys/index.html.slim index 66e90d839..f7796b188 100644 --- a/app/views/vehicle_journeys/index.html.slim +++ b/app/views/vehicle_journeys/index.html.slim @@ -27,6 +27,7 @@    | window.perms = #{raw @perms};    | window.features = #{raw @features};    | window.all_missions = #{(@all_missions.to_json).html_safe}; +  | window.custom_fields = #{(@custom_fields.to_json).html_safe};    | 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 df1eca016..fc65e6cb6 100644 --- a/app/views/vehicle_journeys/show.rabl +++ b/app/views/vehicle_journeys/show.rabl @@ -1,6 +1,6 @@  object @vehicle_journey -[:objectid, :published_journey_name, :published_journey_identifier, :company_id, :comment, :checksum].each do |attr| +[:objectid, :published_journey_name, :published_journey_identifier, :company_id, :comment, :checksum, :custom_fields].each do |attr|    attributes attr, :unless => lambda { |m| m.send( attr).nil?}  end diff --git a/spec/javascript/vehicle_journeys/components/CustomFieldsInputs_spec.js b/spec/javascript/vehicle_journeys/components/CustomFieldsInputs_spec.js new file mode 100644 index 000000000..62013354a --- /dev/null +++ b/spec/javascript/vehicle_journeys/components/CustomFieldsInputs_spec.js @@ -0,0 +1,42 @@ +import React, { Component } from 'react' +import CustomFieldsInputs from '../../../../app/javascript/vehicle_journeys/components/tools/CustomFieldsInputs' +import renderer from 'react-test-renderer' +require('select2') +console.log($().jquery) + +describe('CustomFieldsInputs', () => { +  set('values', () => { +    return {} +  }) + +  set('component', () => { +    let inputs = renderer.create( +      <CustomFieldsInputs +        values={values} +        disabled={false} +        onUpdate={()=>{}} +      /> +    ).toJSON() + +    return inputs +  }) + +  it('should match the snapshot', () => { +    expect(component).toMatchSnapshot() +  }) + +  context('with fields', () => { +    set('values', () => { +      return { +        foo: { +          options: { list_values: ["", "1", "2"] }, +          field_type: "list", +          name: "test" +        } +      } +    }) +    it('should match the snapshot', () => { +      expect(component).toMatchSnapshot() +    }) +  }) +}) diff --git a/spec/javascript/vehicle_journeys/components/__snapshots__/CustomFieldsInputs_spec.js.snap b/spec/javascript/vehicle_journeys/components/__snapshots__/CustomFieldsInputs_spec.js.snap new file mode 100644 index 000000000..c93ec0097 --- /dev/null +++ b/spec/javascript/vehicle_journeys/components/__snapshots__/CustomFieldsInputs_spec.js.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CustomFieldsInputs should match the snapshot 1`] = `<div />`; diff --git a/spec/javascript/vehicle_journeys/reducers/vehicleJourneys_spec.js b/spec/javascript/vehicle_journeys/reducers/vehicleJourneys_spec.js index 44e11aadf..573eebf4f 100644 --- a/spec/javascript/vehicle_journeys/reducers/vehicleJourneys_spec.js +++ b/spec/javascript/vehicle_journeys/reducers/vehicleJourneys_spec.js @@ -89,7 +89,12 @@ describe('vehicleJourneys reducer', () => {      }]      let fakeData = {        published_journey_name: {value: 'test'}, -      published_journey_identifier: {value : ''} +      published_journey_identifier: {value : ''}, +      custom_fields: { +        foo: { +          value: 12 +        } +      }      }      let fakeSelectedJourneyPattern = {id: "1"}      let fakeSelectedCompany = {name: "ALBATRANS"} @@ -115,7 +120,12 @@ describe('vehicleJourneys reducer', () => {        selected: false,        deletable: false,        transport_mode: 'undefined', -      transport_submode: 'undefined' +      transport_submode: 'undefined', +      custom_fields: { +        foo: { +          value: 12 +        } +      }      }, ...state])    }) @@ -345,12 +355,18 @@ describe('vehicleJourneys reducer', () => {    })    it('should handle EDIT_VEHICLEJOURNEY', () => { +    let custom_fields = { +      foo: { +        value: 12 +      } +    }      let fakeData = {        published_journey_name: {value : 'test'}, -      published_journey_identifier: {value: 'test'} +      published_journey_identifier: {value: 'test'}, +      custom_fields: {foo: 12}      }      let fakeSelectedCompany : {name : 'ALBATRANS'} -    let newVJ = Object.assign({}, state[0], {company: fakeSelectedCompany, published_journey_name: fakeData.published_journey_name.value, published_journey_identifier: fakeData.published_journey_identifier.value}) +    let newVJ = Object.assign({}, state[0], {company: fakeSelectedCompany, published_journey_name: fakeData.published_journey_name.value, published_journey_identifier: fakeData.published_journey_identifier.value, custom_fields})      expect(        vjReducer(state, {          type: 'EDIT_VEHICLEJOURNEY', diff --git a/spec/models/chouette/vehicle_journey_spec.rb b/spec/models/chouette/vehicle_journey_spec.rb index 0beec2d81..2a88ac3ce 100644 --- a/spec/models/chouette/vehicle_journey_spec.rb +++ b/spec/models/chouette/vehicle_journey_spec.rb @@ -82,6 +82,7 @@ describe Chouette::VehicleJourney, :type => :model do          item['purchase_windows']         = []          item['footnotes']                = []          item['purchase_windows']         = [] +        item['custom_fields']            = vj.custom_fields          vj.vehicle_journey_at_stops.each do |vjas|            item['vehicle_journey_at_stops'] << vehicle_journey_at_stop_to_state(vjas) @@ -96,7 +97,8 @@ describe Chouette::VehicleJourney, :type => :model do      let(:collection)      { [state] }      it 'should create new vj from state' do -      new_vj = build(:vehicle_journey, objectid: nil, published_journey_name: 'dummy', route: route, journey_pattern: journey_pattern) +      create(:custom_field, code: :energy) +      new_vj = build(:vehicle_journey, objectid: nil, published_journey_name: 'dummy', route: route, journey_pattern: journey_pattern, custom_field_values: {energy: 99})        collection << vehicle_journey_to_state(new_vj)        expect {          Chouette::VehicleJourney.state_update(route, collection) @@ -112,6 +114,7 @@ describe Chouette::VehicleJourney, :type => :model do        expect(collection.last['objectid']).to eq obj.objectid        expect(obj.published_journey_name).to eq 'dummy' +      expect(obj.custom_fields["energy"]["value"]).to eq 99      end      it 'should expect local times' do @@ -231,11 +234,13 @@ describe Chouette::VehicleJourney, :type => :model do      it 'should update vj attributes from state' do        state['published_journey_name']       = 'edited_name'        state['published_journey_identifier'] = 'edited_identifier' +      state['custom_fields'] = {energy: {value: 99}}        Chouette::VehicleJourney.state_update(route, collection)        expect(state['errors']).to be_nil        expect(vehicle_journey.reload.published_journey_name).to eq state['published_journey_name']        expect(vehicle_journey.reload.published_journey_identifier).to eq state['published_journey_identifier'] +      expect(vehicle_journey.reload.custom_field_value("energy")).to eq 99      end      it 'should return errors when validation failed' do diff --git a/spec/models/custom_field_spec.rb b/spec/models/custom_field_spec.rb index 80873683c..6ebf994db 100644 --- a/spec/models/custom_field_spec.rb +++ b/spec/models/custom_field_spec.rb @@ -17,8 +17,15 @@ RSpec.describe CustomField, type: :model do    context "custom fields for a resource" do -    let!( :fields ){ (1..2).map{ create :custom_field } } -    it { expect(vj.custom_fields).to eq(fields) } +    let!( :fields ){ [create(:custom_field), create(:custom_field, code: :energy)] } +    let!( :instance_fields ){ +      { +        fields[0].code => fields[0].slice(:code, :name, :field_type, :options).update(value: nil), +        "energy" => fields[1].slice(:code, :name, :field_type, :options).update(value: 99) +      } +    } +    it { expect(Chouette::VehicleJourney.custom_fields).to eq(fields) } +    it { expect(vj.custom_fields).to eq(instance_fields) }    end    context "custom field_values for a resource" do | 
