diff options
| author | Vlatka Pavisic | 2017-01-03 16:55:24 +0100 |
|---|---|---|
| committer | Vlatka Pavisic | 2017-01-03 16:55:24 +0100 |
| commit | 63e20d284855f9e40706e94dc8d17558eb18e1e4 (patch) | |
| tree | 514e02de0f18c57a140eb603802afcb29d4375ec | |
| parent | bac2e1b1bd9111580d161b3282f52460d49783ca (diff) | |
| download | chouette-core-63e20d284855f9e40706e94dc8d17558eb18e1e4.tar.bz2 | |
Refs #2265 Refs #2262 : Calendars search and CRUD
| -rw-r--r-- | app/assets/stylesheets/main/calendars.sass | 4 | ||||
| -rw-r--r-- | app/controllers/calendars_controller.rb | 6 | ||||
| -rw-r--r-- | app/helpers/breadcrumb_helper.rb | 2 | ||||
| -rw-r--r-- | app/models/calendar.rb | 117 | ||||
| -rw-r--r-- | app/views/calendars/_date_fields.html.slim | 4 | ||||
| -rw-r--r-- | app/views/calendars/_date_value_fields.html.slim | 14 | ||||
| -rw-r--r-- | app/views/calendars/_form.html.slim | 29 | ||||
| -rw-r--r-- | app/views/calendars/_period_fields.html.slim | 18 | ||||
| -rw-r--r-- | app/views/calendars/index.html.slim | 7 | ||||
| -rw-r--r-- | app/views/calendars/new.html.slim | 2 | ||||
| -rw-r--r-- | app/views/calendars/show.html.slim | 28 | ||||
| -rw-r--r-- | app/views/referentials/_period_fields.html.slim | 1 | ||||
| -rw-r--r-- | config/locales/calendars.en.yml | 3 | ||||
| -rw-r--r-- | config/locales/calendars.fr.yml | 3 | ||||
| -rw-r--r-- | spec/models/calendar_spec.rb | 36 |
15 files changed, 218 insertions, 56 deletions
diff --git a/app/assets/stylesheets/main/calendars.sass b/app/assets/stylesheets/main/calendars.sass index 214cff1b1..298ce2a62 100644 --- a/app/assets/stylesheets/main/calendars.sass +++ b/app/assets/stylesheets/main/calendars.sass @@ -1,6 +1,10 @@ #calendar_form .btn margin: 5px + #delete-btn + margin-top: 23px + .well + padding-left: 50px #calendar_search_form button diff --git a/app/controllers/calendars_controller.rb b/app/controllers/calendars_controller.rb index 697d8a507..579a48e1d 100644 --- a/app/controllers/calendars_controller.rb +++ b/app/controllers/calendars_controller.rb @@ -7,7 +7,7 @@ class CalendarsController < BreadcrumbController private def calendar_params - params.require(:calendar).permit(:id, :name, :short_name, :shared, periods_attributes: [:id, :begin, :end, :_destroy], dates: []) + params.require(:calendar).permit(:id, :name, :short_name, :shared, periods_attributes: [:id, :begin, :end, :_destroy], date_values_attributes: [:id, :value, :_destroy]) end def sort_column @@ -31,10 +31,6 @@ class CalendarsController < BreadcrumbController def collection return @calendars if @calendars - if params[:q] - params[:q].delete(:shared_eq) if params[:q][:shared_eq] == '' - params[:q].delete(:short_name_cont) if params[:q][:short_name_cont] == '' - end @q = current_organisation.calendars.search(params[:q]) calendars = @q.result diff --git a/app/helpers/breadcrumb_helper.rb b/app/helpers/breadcrumb_helper.rb index da8cf8425..6ff4845f9 100644 --- a/app/helpers/breadcrumb_helper.rb +++ b/app/helpers/breadcrumb_helper.rb @@ -73,7 +73,7 @@ module BreadcrumbHelper end def calendar_breadcrumb(action) - add_breadcrumb I18n.t('calendars.index.title') + add_breadcrumb I18n.t('calendars.index.title'), calendars_path add_breadcrumb @calendar.name if %i(show edit).include? action end diff --git a/app/models/calendar.rb b/app/models/calendar.rb index 9c228ad69..d616bce10 100644 --- a/app/models/calendar.rb +++ b/app/models/calendar.rb @@ -3,18 +3,23 @@ class Calendar < ActiveRecord::Base validates_presence_of :name, :short_name, :organisation validates_uniqueness_of :short_name - validate :date_not_in_date_ranges, :dates_uniqueness after_initialize :init_dates_and_date_ranges scope :contains_date, ->(date) { where('date ? = any (dates) OR date ? <@ any (date_ranges)', date, date) } + scope :shared, -> { where(shared: true) } + scope :by_organisation(org_id), -> { where(organisation_id: org_id) } def init_dates_and_date_ranges self.dates ||= [] self.date_ranges ||= [] end + def self.ransackable_scopes(auth_object = nil) + [:contains_date] + end + private_class_method :ransackable_scopes class Period include ActiveAttr::Model @@ -23,7 +28,7 @@ class Calendar < ActiveRecord::Base attribute :begin, type: Date attribute :end, type: Date - validates :begin, :end, presence: true + validates_presence_of :begin, :end validate :check_end_greather_than_begin def check_end_greather_than_begin @@ -105,7 +110,7 @@ class Calendar < ActiveRecord::Base periods.each do |period| if period.intersect?(periods) - period.errors.add(:base, I18n.t('calendars.errors.overlapped_period')) + period.errors.add(:base, I18n.t('calendars.errors.overlapped_periods')) periods_are_valid = false end end @@ -141,34 +146,110 @@ class Calendar < ActiveRecord::Base private :clear_periods - def self.new_from from - from.dup.tap do |metadata| - metadata.referential_id = nil +### dates + + class DateValue + include ActiveAttr::Model + + attribute :id, type: Integer + attribute :value, type: Date + + validates_presence_of :value + + def self.from_date(index, date) + DateValue.new id: index, value: date + end + + # Stuff required for coocon + def new_record? + !persisted? end + + def persisted? + id.present? + end + + def mark_for_destruction + self._destroy = true + end + + attribute :_destroy, type: Boolean + alias_method :marked_for_destruction?, :_destroy end - private - def date_not_in_date_ranges - errors.add(:dates, I18n.t('activerecord.errors.models.calendar.attributes.dates.date_in_date_ranges')) if dates && date_ranges && dates_and_date_ranges_overlap? + # Required by coocon + def build_date_value + DateValue.new end - def dates_and_date_ranges_overlap? - overlap = false - dates.each do |date| + def date_values + @date_values ||= init_date_values + end + + def init_date_values + if dates + dates.each_with_index.map { |d, index| DateValue.from_date(index, d) } + else + [] + end + end + private :init_date_values + + validate :validate_date_values + + def validate_date_values + date_values_are_valid = true + + unless date_values.all?(&:valid?) + date_values_are_valid = false + end + + date_values.each do |date_value| + if date_values.count { |d| d.value == date_value.value } > 1 + date_value.errors.add(:base, I18n.t('activerecord.errors.models.calendar.attributes.dates.date_in_dates')) + date_values_are_valid = false + end date_ranges.each do |date_range| - overlap = true if date_range.cover? date + if date_range.cover? date_value.value + date_value.errors.add(:base, I18n.t('activerecord.errors.models.calendar.attributes.dates.date_in_date_ranges')) + date_values_are_valid = false + end end end - overlap + + unless date_values_are_valid + errors.add(:date_values, :invalid) + end end - def dates_uniqueness - errors.add(:dates, I18n.t('activerecord.errors.models.calendar.attributes.dates.date_in_dates')) if dates && dates.length > dates.uniq.length + def date_values_attributes=(attributes = {}) + @date_values = [] + attributes.each do |index, date_value_attribute| + date_value = DateValue.new(date_value_attribute.merge(id: index)) + @date_values << date_value unless date_value.marked_for_destruction? + end + + dates_will_change! end - def self.ransackable_scopes(auth_object = nil) - [:contains_date] + before_validation :fill_dates + + def fill_dates + if @date_values + self.dates = @date_values.map(&:value).compact.sort + end end + + after_save :clear_date_values + + def clear_date_values + @date_values = nil + end + + private :clear_date_values + +### + end class Range diff --git a/app/views/calendars/_date_fields.html.slim b/app/views/calendars/_date_fields.html.slim deleted file mode 100644 index 8fd1b5f14..000000000 --- a/app/views/calendars/_date_fields.html.slim +++ /dev/null @@ -1,4 +0,0 @@ -.nested-fields - = f.input :date, as: :date, html5: true - /= link_to_remove_association f, data: { confirm: t('are_you_sure') } do - span.fa.fa-trash diff --git a/app/views/calendars/_date_value_fields.html.slim b/app/views/calendars/_date_value_fields.html.slim new file mode 100644 index 000000000..3a9cdb7bd --- /dev/null +++ b/app/views/calendars/_date_value_fields.html.slim @@ -0,0 +1,14 @@ +.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 + .row + .col-xs-3 + = f.input :value, as: :date, html5: true, label: t('simple_form.labels.calendar.date_value') + .col-xs-1.end.text-right#delete-btn + = link_to_remove_association f, class: 'btn btn-danger', data: { confirm: t('are_you_sure') } do + span.fa.fa-trash + diff --git a/app/views/calendars/_form.html.slim b/app/views/calendars/_form.html.slim index e490441ed..1a1172a7b 100644 --- a/app/views/calendars/_form.html.slim +++ b/app/views/calendars/_form.html.slim @@ -4,22 +4,23 @@ = simple_form_for @calendar, html: { class: 'form-horizontal' } do |f| = f.input :name = f.input :short_name - / = f.label :dates - / = f.simple_fields_for :dates do |date| - / = render 'date_fields', f: date - / .links - / = link_to_add_association '+', f, :dates - = f.label :date_ranges - = f.simple_fields_for :periods do |period| - = render 'period_fields', f: period - .links - = link_to_add_association '+', f, :periods - + .form-group + = f.label :dates + .well + = f.simple_fields_for :date_values do |date_value| + = render 'date_value_fields', f: date_value + .links + = link_to_add_association t('simple_form.labels.calendar.add_a_date'), f, :date_values, class: 'btn btn-primary btn-xs' + .form-group + = f.label :date_ranges + .well + = f.simple_fields_for :periods do |period| + = render 'period_fields', f: period + .links + = link_to_add_association t('simple_form.labels.calendar.add_a_date_range'), f, :periods, class: 'btn btn-primary btn-xs' + = f.input :shared .form-actions = f.button :submit, as: :button, class: 'btn btn-info' = link_to t('cancel'), calendars_path, class: 'btn btn-default' - = f.input :shared - -/ TODO : cocoon diff --git a/app/views/calendars/_period_fields.html.slim b/app/views/calendars/_period_fields.html.slim index 50413ef20..024d09de2 100644 --- a/app/views/calendars/_period_fields.html.slim +++ b/app/views/calendars/_period_fields.html.slim @@ -1,6 +1,16 @@ .nested-fields - = f.input :begin, as: :date, html5: true, label: t('simple_form.labels.calendar.ranges.begin') - = f.input :end, as: :date, html5: true, label: t('simple_form.labels.calendar.ranges.end') - = link_to_remove_association f, data: { confirm: t('are_you_sure') } do - span.fa.fa-trash + - if f.object.errors.has_key? :base + .row + .col-lg-12 + .alert.alert-danger + - f.object.errors[:base].each do |message| + p.small = message + .row + .col-xs-4 + = f.input :begin, as: :date, html5: true, label: t('simple_form.labels.calendar.ranges.begin') + .col-xs-3 + = f.input :end, as: :date, html5: true, label: t('simple_form.labels.calendar.ranges.end') + .col-xs-1.text-right#delete-btn + = link_to_remove_association f, class: 'btn btn-danger', data: { confirm: t('are_you_sure') } do + span.fa.fa-trash diff --git a/app/views/calendars/index.html.slim b/app/views/calendars/index.html.slim index a857078a1..9696e3c0b 100644 --- a/app/views/calendars/index.html.slim +++ b/app/views/calendars/index.html.slim @@ -21,3 +21,10 @@ #calendars = render 'calendars' + +- content_for :sidebar do + ul.actions + li + - if policy(Calendar).create? + = link_to t('calendars.actions.new'), new_calendar_path, class: 'add' + br diff --git a/app/views/calendars/new.html.slim b/app/views/calendars/new.html.slim index ccae7ce89..f827e2eb6 100644 --- a/app/views/calendars/new.html.slim +++ b/app/views/calendars/new.html.slim @@ -1,5 +1,3 @@ = title_tag t('.title') -= @calendar.inspect - = render 'form' diff --git a/app/views/calendars/show.html.slim b/app/views/calendars/show.html.slim index 419cec78e..169d59f57 100644 --- a/app/views/calendars/show.html.slim +++ b/app/views/calendars/show.html.slim @@ -6,12 +6,28 @@ p p label => "#{Calendar.human_attribute_name('short_name')} : " = @calendar.short_name -p - label => "#{Calendar.human_attribute_name('date_ranges')} : " - = @calendar.date_ranges -p - label => "#{Calendar.human_attribute_name('dates')} : " - = @calendar.dates + +.row + .col-xs-4 + p + label => "#{Calendar.human_attribute_name('dates')} : " + table.table.table-condensed + tbody + - @calendar.dates.each do |date| + tr + td= l date + p + label => "#{Calendar.human_attribute_name('date_ranges')} : " + table.table.table-condensed + thead + th= t('simple_form.labels.calendar.ranges.begin') + th= t('simple_form.labels.calendar.ranges.end') + tbody + - @calendar.date_ranges.each do |date_range| + tr + td= l date_range.begin + td= l date_range.end + p label => "#{Calendar.human_attribute_name('shared')} : " = @calendar.shared diff --git a/app/views/referentials/_period_fields.html.slim b/app/views/referentials/_period_fields.html.slim index 9d92f92ce..6658cd4aa 100644 --- a/app/views/referentials/_period_fields.html.slim +++ b/app/views/referentials/_period_fields.html.slim @@ -14,3 +14,4 @@ .col-lg-2.col-md-2.col-sm-2.col-xs-2.text-right style='margin-top:23px' = link_to_remove_association f, class: 'btn btn-danger', data: { confirm: 'Etes-vous sûr(e) ?' } do span.fa.fa-trash + diff --git a/config/locales/calendars.en.yml b/config/locales/calendars.en.yml index b7ad5df26..fe35f3dc4 100644 --- a/config/locales/calendars.en.yml +++ b/config/locales/calendars.en.yml @@ -38,6 +38,9 @@ en: dates: Dates shared: Shared date_ranges: Date ranges + date_value: Date + add_a_date: Add a date + add_a_date_range: Add a date range ranges: begin: Beginning end: End diff --git a/config/locales/calendars.fr.yml b/config/locales/calendars.fr.yml index 358ae0d46..1f96972c2 100644 --- a/config/locales/calendars.fr.yml +++ b/config/locales/calendars.fr.yml @@ -38,6 +38,9 @@ fr: dates: Dates shared: Partagé date_ranges: Intervalles de dates + date_value: Date + add_a_date: Ajouter une date + add_a_date_range: Ajouter un intervalle de dates ranges: begin: Début end: Fin diff --git a/spec/models/calendar_spec.rb b/spec/models/calendar_spec.rb index 2557cdb93..36981961f 100644 --- a/spec/models/calendar_spec.rb +++ b/spec/models/calendar_spec.rb @@ -15,7 +15,6 @@ RSpec.describe Calendar, :type => :model do expect { calendar.save! }.to raise_error(ActiveRecord::RecordInvalid) - expect(calendar.errors.messages[:dates]).to eq([I18n.t('activerecord.errors.models.calendar.attributes.dates.date_in_date_ranges')]) end it 'validates that there are no duplicates in dates' do @@ -23,7 +22,6 @@ RSpec.describe Calendar, :type => :model do expect { calendar.save! }.to raise_error(ActiveRecord::RecordInvalid) - expect(calendar.errors.messages[:dates]).to eq([I18n.t('activerecord.errors.models.calendar.attributes.dates.date_in_dates')]) end end @@ -112,5 +110,39 @@ RSpec.describe Calendar, :type => :model do end end + describe 'DateValue' do + + subject { date_value } + + def date_value(attributes = {}) + return @date_value if attributes.empty? and @date_value + Calendar::DateValue.new(attributes).tap do |date_value| + @date_value = date_value if attributes.empty? + end + end + + it 'should support mark_for_destruction (required by cocoon)' do + date_value.mark_for_destruction + expect(date_value).to be_marked_for_destruction + end + + it 'should support _destroy attribute (required by coocon)' do + date_value._destroy = true + expect(date_value).to be_marked_for_destruction + end + + it 'should support new_record? (required by cocoon)' do + expect(Calendar::DateValue.new).to be_new_record + expect(date_value(id: 42)).not_to be_new_record + end + + it 'should cast value as date attribute' do + expect(date_value(value: '2017-01-03').value).to eq(Date.new(2017,01,03)) + end + + it { is_expected.to validate_presence_of(:value) } + + end + end |
