aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/assets/stylesheets/main/calendars.sass4
-rw-r--r--app/controllers/calendars_controller.rb6
-rw-r--r--app/helpers/breadcrumb_helper.rb2
-rw-r--r--app/models/calendar.rb117
-rw-r--r--app/views/calendars/_date_fields.html.slim4
-rw-r--r--app/views/calendars/_date_value_fields.html.slim14
-rw-r--r--app/views/calendars/_form.html.slim29
-rw-r--r--app/views/calendars/_period_fields.html.slim18
-rw-r--r--app/views/calendars/index.html.slim7
-rw-r--r--app/views/calendars/new.html.slim2
-rw-r--r--app/views/calendars/show.html.slim28
-rw-r--r--app/views/referentials/_period_fields.html.slim1
-rw-r--r--config/locales/calendars.en.yml3
-rw-r--r--config/locales/calendars.fr.yml3
-rw-r--r--spec/models/calendar_spec.rb36
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