diff options
| author | Zog | 2018-01-24 16:56:16 +0100 |
|---|---|---|
| committer | cedricnjanga | 2018-02-06 11:10:18 -0800 |
| commit | f07802715562b31d15e31b5be90dbeac29cef227 (patch) | |
| tree | f64dc33cb1bc35acaeca9261012d10516a7daf2a | |
| parent | 5253b169da36a3de270f00038597b007035f09dd (diff) | |
| download | chouette-core-f07802715562b31d15e31b5be90dbeac29cef227.tar.bz2 | |
Refs #5682 @3h; Use same UI as for timetables
28 files changed, 637 insertions, 167 deletions
diff --git a/app/assets/stylesheets/modules/_timetables.sass b/app/assets/stylesheets/modules/_timetables.sass index b06972ef9..8999456ec 100644 --- a/app/assets/stylesheets/modules/_timetables.sass +++ b/app/assets/stylesheets/modules/_timetables.sass @@ -30,7 +30,7 @@ .t2e-item .th - padding: 6px 0 0 0 + padding: 4px 0 0 0 border-color: $darkgrey border-top-width: 2px diff --git a/app/controllers/calendars_controller.rb b/app/controllers/calendars_controller.rb index eaff3b0c6..3b7b6de15 100644 --- a/app/controllers/calendars_controller.rb +++ b/app/controllers/calendars_controller.rb @@ -1,8 +1,11 @@ class CalendarsController < ChouetteController include PolicyChecker + include TimeTablesHelper + defaults resource_class: Calendar before_action :ransack_contains_date, only: [:index] respond_to :html + respond_to :json, only: :show respond_to :js, only: :index belongs_to :workgroup @@ -21,6 +24,32 @@ class CalendarsController < ChouetteController end end + def month + @date = params['date'] ? Date.parse(params['date']) : Date.today + @calendar = resource + end + + def create + create! do + if @calendar.valid? && has_feature?('application_days_on_calendars') + redirect_to([:edit, @calendar]) + return + end + end + end + + def update + if params[:calendar] + super + else + state = JSON.parse request.raw_post + resource.state_update state + respond_to do |format| + format.json { render json: state, status: state['errors'] ? :unprocessable_entity : :ok } + end + end + end + private def decorate_calendars(calendars) diff --git a/app/helpers/time_tables_helper.rb b/app/helpers/time_tables_helper.rb index b380a2b0a..1be1fa5a1 100644 --- a/app/helpers/time_tables_helper.rb +++ b/app/helpers/time_tables_helper.rb @@ -84,7 +84,7 @@ module TimeTablesHelper end unless first.wday == first_weekday first.upto(last) do |cur| - cell_text, cell_attrs = block.call(cur) + cell_text, cell_attrs = yield cur cell_text ||= cur.mday cell_attrs ||= {} cell_attrs[:headers] = th_id(cur, options[:table_id]) diff --git a/app/javascript/packs/calendars/edit.js b/app/javascript/packs/calendars/edit.js new file mode 100644 index 000000000..bd09657ec --- /dev/null +++ b/app/javascript/packs/calendars/edit.js @@ -0,0 +1,74 @@ +import React from 'react' +import { render } from 'react-dom' +import { Provider } from 'react-redux' +import { createStore } from 'redux' +import timeTablesApp from '../../time_tables/reducers' +import App from '../../time_tables/containers/App' +import clone from '../../helpers/clone' + +const actionType = clone(window, "actionType", true) + +// logger, DO NOT REMOVE +// var applyMiddleware = require('redux').applyMiddleware +// var createLogger = require('redux-logger') +// var thunkMiddleware = require('redux-thunk').default +// var promise = require('redux-promise') + +let initialState = { + status: { + actionType: actionType, + policy: window.perms, + fetchSuccess: true, + isFetching: false + }, + timetable: { + current_month: [], + current_periode_range: '', + periode_range: [], + time_table_periods: [], + time_table_dates: [] + }, + metas: { + comment: '', + day_types: [], + initial_tags: [] + }, + pagination: { + stateChanged: false, + currentPage: '', + periode_range: [] + }, + modal: { + type: '', + modalProps: { + active: false, + begin: { + day: '01', + month: '01', + year: String(new Date().getFullYear()) + }, + end: { + day: '01', + month: '01', + year: String(new Date().getFullYear()) + }, + index: false, + error: '' + }, + confirmModal: {} + } +} +// const loggerMiddleware = createLogger() + +let store = createStore( + timeTablesApp, + initialState, + // applyMiddleware(thunkMiddleware, promise, loggerMiddleware) +) + +render( + <Provider store={store}> + <App /> + </Provider>, + document.getElementById('periods') +) diff --git a/app/javascript/time_tables/actions/index.js b/app/javascript/time_tables/actions/index.js index 87c7a3e8d..70f548174 100644 --- a/app/javascript/time_tables/actions/index.js +++ b/app/javascript/time_tables/actions/index.js @@ -246,7 +246,8 @@ const actions = { return error }, fetchTimeTables: (dispatch, nextPage) => { - let urlJSON = window.location.pathname.split('/', 5).join('/') + let urlJSON = window.timetablesUrl || window.location.pathname.split('/', 5).join('/') + if(nextPage) { urlJSON += "/month.json?date=" + nextPage }else{ @@ -277,7 +278,7 @@ const actions = { let strDayTypes = actions.arrayToStrDayTypes(metas.day_types) metas.day_types = strDayTypes let sentState = assign({}, timetable, metas) - let urlJSON = window.location.pathname.split('/', 5).join('/') + let urlJSON = window.timetablesUrl || window.location.pathname.split('/', 5).join('/') let hasError = false fetch(urlJSON + '.json', { credentials: 'same-origin', diff --git a/app/javascript/time_tables/components/Metas.js b/app/javascript/time_tables/components/Metas.js index 4170ba493..3c6848d27 100644 --- a/app/javascript/time_tables/components/Metas.js +++ b/app/javascript/time_tables/components/Metas.js @@ -27,7 +27,7 @@ export default function Metas({metas, onUpdateDayTypes, onUpdateComment, onUpdat </div> {/* color */} - <div className="form-group"> + {metas.color !== undefined && <div className="form-group"> <label htmlFor="" className="control-label col-sm-4">{I18n.activerecord.attributes.time_table.color}</label> <div className="col-sm-8"> <div className="dropdown color_selector"> @@ -69,10 +69,10 @@ export default function Metas({metas, onUpdateDayTypes, onUpdateComment, onUpdat </div> </div> </div> - </div> + </div>} {/* tags */} - <div className="form-group"> + {metas.tags !== undefined && <div className="form-group"> <label htmlFor="" className="control-label col-sm-4">{I18n.activerecord.attributes.time_table.tag_list}</label> <div className="col-sm-8"> <TagsSelect2 @@ -82,15 +82,15 @@ export default function Metas({metas, onUpdateDayTypes, onUpdateComment, onUpdat onUnselect2Tags={(e) => onUnselect2Tags(e)} /> </div> - </div> + </div>} {/* calendar */} - <div className="form-group"> + {metas.calendar !== null && <div className="form-group"> <label htmlFor="" className="control-label col-sm-4">{I18n.activerecord.attributes.time_table.calendar}</label> <div className="col-sm-8"> <span>{metas.calendar ? metas.calendar.name : I18n.time_tables.edit.metas.no_calendar}</span> </div> - </div> + </div>} {/* day_types */} <div className="form-group"> diff --git a/app/models/calendar.rb b/app/models/calendar.rb index e0f0f03da..84b569ab4 100644 --- a/app/models/calendar.rb +++ b/app/models/calendar.rb @@ -6,6 +6,7 @@ class Calendar < ActiveRecord::Base include DateSupport include PeriodSupport include ApplicationDaysSupport + include TimetableSupport has_paper_trail class_name: 'PublicVersion' belongs_to :organisation @@ -17,16 +18,29 @@ class Calendar < ActiveRecord::Base has_many :time_tables scope :contains_date, ->(date) { where('date ? = any (dates) OR date ? <@ any (date_ranges)', date, date) } - before_create :set_default_days + + after_initialize :set_defaults def self.ransackable_scopes(auth_object = nil) [:contains_date] end - def set_default_days + def self.state_permited_attributes item + {name: item["comment"]} + end + + def set_defaults + self.excluded_dates ||= [] self.int_day_types ||= EVERYDAY end + def human_attribute_name(*args) + self.class.human_attribute_name(*args) + end + + def shortcuts_update(date=nil) + end + def convert_to_time_table Chouette::TimeTable.new.tap do |tt| self.dates.each do |d| @@ -39,4 +53,65 @@ class Calendar < ActiveRecord::Base end end + def include_in_dates?(day) + self.dates.include? day + end + + def excluded_date?(day) + self.excluded_dates.include? day + end + + def update_in_out date, in_out + if in_out + self.excluded_dates.delete date + self.dates << date unless include_in_dates?(date) + else + self.dates.delete date + self.excluded_dates << date unless excluded_date?(date) + end + date + end + + def included_days + dates + end + + def excluded_days + excluded_dates + end + + def saved_dates + Hash[*self.dates.each_with_index.to_a.map(&:reverse).flatten] + end + + def all_dates + (dates + excluded_dates).sort.each_with_index.map do |d, i| + OpenStruct.new(id: i, date: d, in_out: include_in_dates?(d)) + end + end + + def find_date_by_id id + self.dates[id] + end + + def destroy_date date + self.dates -= [date] + end + + def create_date in_out:, date: + update_in_out date, in_out + end + + def find_period_by_id id + self.periods.find{|p| p.id == id} + end + + def build_period + self.periods << Calendar::Period.new(id: self.periods.count + 1) + self.periods.last + end + + def destroy_period period + @periods = self.periods.select{|p| p.end != period.end || p.begin != period.begin} + end end diff --git a/app/models/calendar/period.rb b/app/models/calendar/period.rb index 56ab722fe..8b3e4109b 100644 --- a/app/models/calendar/period.rb +++ b/app/models/calendar/period.rb @@ -10,6 +10,11 @@ class Calendar < ActiveRecord::Base validates_presence_of :begin, :end validate :check_end_greather_than_begin + alias_method :period_start, :begin + alias_method :period_end, :end + alias_method :period_start=, :begin= + alias_method :period_end=, :end= + def check_end_greather_than_begin if self.begin && self.end && self.begin >= self.end errors.add(:base, I18n.t('calendars.errors.short_period')) diff --git a/app/models/chouette/time_table.rb b/app/models/chouette/time_table.rb index 1a1972113..8113457ec 100644 --- a/app/models/chouette/time_table.rb +++ b/app/models/chouette/time_table.rb @@ -5,6 +5,7 @@ module Chouette include TimeTableRestrictions include ObjectidSupport include ApplicationDaysSupport + include TimetableSupport # FIXME http://jira.codehaus.org/browse/JRUBY-6358 self.primary_key = "id" @@ -82,72 +83,36 @@ module Chouette end end - def state_update state - update_attributes(self.class.state_permited_attributes(state)) - self.tag_list = state['tags'].collect{|t| t['name']}.join(', ') - self.calendar_id = nil unless state['calendar'] - - days = state['day_types'].split(',') - Date::DAYNAMES.map(&:underscore).each do |name| - prefix = human_attribute_name(name).first(2) - send("#{name}=", days.include?(prefix)) - end - - saved_dates = Hash[self.dates.collect{ |d| [d.id, d.date]}] - cmonth = Date.parse(state['current_periode_range']) - - state['current_month'].each do |d| - date = Date.parse(d['date']) - checked = d['include_date'] || d['excluded_date'] - in_out = d['include_date'] ? true : false - - date_id = saved_dates.key(date) - time_table_date = self.dates.find(date_id) if date_id + def find_date_by_id id + self.dates.find id + end - next if !checked && !time_table_date - # Destroy date if no longer checked - next if !checked && time_table_date.destroy + def destroy_date date + date.destroy + end - # Create new date - unless time_table_date - time_table_date = self.dates.create({in_out: in_out, date: date}) - end - # Update in_out - if in_out != time_table_date.in_out - time_table_date.update_attributes({in_out: in_out}) - end + def update_in_out date, in_out + if in_out != date.in_out + date.update_attributes({in_out: in_out}) end - - self.state_update_periods state['time_table_periods'] - self.save end - def state_update_periods state_periods - state_periods.each do |item| - period = self.periods.find(item['id']) if item['id'] - next if period && item['deleted'] && period.destroy - period ||= self.periods.build - - period.period_start = Date.parse(item['period_start']) - period.period_end = Date.parse(item['period_end']) + def find_period_by_id id + self.periods.find id + end - if period.changed? - period.save - item['id'] = period.id - end - end + def build_period + periods.build + end - state_periods.delete_if {|item| item['deleted']} + def destroy_period period + period.destroy end def self.state_permited_attributes item item.slice('comment', 'color').to_hash end - def presenter - @presenter ||= ::TimeTablePresenter.new( self) - end - def self.start_validity_period [Chouette::TimeTable.minimum(:start_date)].compact.min end @@ -168,20 +133,6 @@ module Chouette self.save end - def month_inspect(date) - (date.beginning_of_month..date.end_of_month).map do |d| - { - day: I18n.l(d, format: '%A'), - date: d.to_s, - wday: d.wday, - wnumber: d.strftime("%W").to_s, - mday: d.mday, - include_date: include_in_dates?(d), - excluded_date: excluded_date?(d) - } - end - end - def save_shortcuts shortcuts_update self.update_column(:start_date, start_date) @@ -361,6 +312,17 @@ module Chouette days.sort end + def create_date in_out:, date: + self.dates.create in_out: in_out, date: date + end + + def saved_dates + Hash[self.dates.collect{ |d| [d.id, d.date]}] + end + + def all_dates + dates + end # produce a copy of periods without anyone overlapping or including another def optimize_overlapping_periods diff --git a/app/models/concerns/application_days_support.rb b/app/models/concerns/application_days_support.rb index 425cba5bf..348436aa4 100644 --- a/app/models/concerns/application_days_support.rb +++ b/app/models/concerns/application_days_support.rb @@ -35,6 +35,10 @@ module ApplicationDaysSupport end end + def valid_day? wday + valid_days.include?(wday) + end + def self.valid_days(int_day_types) # Build an array with day of calendar week (1-7, Monday is 1). [].tap do |valid_days| diff --git a/app/models/concerns/date_support.rb b/app/models/concerns/date_support.rb index fbfe19af1..5c66cb1a9 100644 --- a/app/models/concerns/date_support.rb +++ b/app/models/concerns/date_support.rb @@ -38,9 +38,12 @@ module DateSupport date_values_are_valid = false end date_ranges.each do |date_range| - 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 + if date_range.cover?(date_value.value) + excluded_day = self.respond_to?(:valid_day?) && !self.valid_day?(date_value.value.wday) + unless excluded_day + 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 end @@ -77,4 +80,4 @@ module DateSupport private :clear_date_values end -end
\ No newline at end of file +end diff --git a/app/models/concerns/timetable_support.rb b/app/models/concerns/timetable_support.rb new file mode 100644 index 000000000..8c49723fe --- /dev/null +++ b/app/models/concerns/timetable_support.rb @@ -0,0 +1,149 @@ +module TimetableSupport + extend ActiveSupport::Concern + + def presenter + @presenter ||= ::TimeTablePresenter.new( self) + end + + def periods_max_date + return nil if self.periods.empty? + + min_start = self.periods.map(&:period_start).compact.min + max_end = self.periods.map(&:period_end).compact.max + result = nil + + if max_end && min_start + max_end.downto( min_start) do |date| + if self.valid_days.include?(date.cwday) && !self.excluded_date?(date) + result = date + break + end + end + end + result + end + + def periods_min_date + return nil if self.periods.empty? + + min_start = self.periods.map(&:period_start).compact.min + max_end = self.periods.map(&:period_end).compact.max + result = nil + + if max_end && min_start + min_start.upto(max_end) do |date| + if self.valid_days.include?(date.cwday) && !self.excluded_date?(date) + result = date + break + end + end + end + result + end + + def bounding_dates + bounding_min = self.all_dates.select{|d| d.in_out}.map(&:date).compact.min + bounding_max = self.all_dates.select{|d| d.in_out}.map(&:date).compact.max + + unless self.periods.empty? + bounding_min = periods_min_date if periods_min_date && + (bounding_min.nil? || (periods_min_date < bounding_min)) + + bounding_max = periods_max_date if periods_max_date && + (bounding_max.nil? || (bounding_max < periods_max_date)) + end + + [bounding_min, bounding_max].compact + end + + def month_inspect(date) + (date.beginning_of_month..date.end_of_month).map do |d| + { + day: I18n.l(d, format: '%A'), + date: d.to_s, + wday: d.wday, + wnumber: d.strftime("%W").to_s, + mday: d.mday, + include_date: include_in_dates?(d), + excluded_date: excluded_date?(d) + } + end + end + + def include_in_dates?(day) + self.dates.any?{ |d| d.date === day && d.in_out == true } + end + + def excluded_date?(day) + self.dates.any?{ |d| d.date === day && d.in_out == false } + end + + def include_in_overlap_dates?(day) + return false if self.excluded_date?(day) + + self.all_dates.any?{ |d| d.date === day} \ + && self.periods.any?{ |period| period.period_start <= day && day <= period.period_end && valid_days.include?(day.cwday) } + end + + def include_in_periods?(day) + self.periods.any?{ |period| period.period_start <= day && + day <= period.period_end && + valid_days.include?(day.cwday) && + ! excluded_date?(day) } + end + + def state_update_periods state_periods + state_periods.each do |item| + period = self.find_period_by_id(item['id']) if item['id'] + next if period && item['deleted'] && self.destroy_period(period) + period ||= self.build_period + + period.period_start = Date.parse(item['period_start']) + period.period_end = Date.parse(item['period_end']) + + period.save if period === ActiveRecord::Base && period.changed? + + item['id'] = period.id + end + + state_periods.delete_if {|item| item['deleted']} + end + + def state_update state + update_attributes(self.class.state_permited_attributes(state)) + self.tag_list = state['tags'].collect{|t| t['name']}.join(', ') if state['tags'] + self.calendar_id = nil if self.respond_to?(:calendar_id) && !state['calendar'] + + days = state['day_types'].split(',') + Date::DAYNAMES.map(&:underscore).each do |name| + prefix = human_attribute_name(name).first(2) + send("#{name}=", days.include?(prefix)) + end + + cmonth = Date.parse(state['current_periode_range']) + + state['current_month'].each do |d| + date = Date.parse(d['date']) + checked = d['include_date'] || d['excluded_date'] + in_out = d['include_date'] ? true : false + + date_id = saved_dates.key(date) + time_table_date = self.find_date_by_id(date_id) if date_id + + next if !checked && !time_table_date + # Destroy date if no longer checked + next if !checked && destroy_date(time_table_date) + + # Create new date + unless time_table_date + time_table_date = self.create_date in_out: in_out, date: date + end + # Update in_out + self.update_in_out time_table_date, in_out + end + + self.state_update_periods state['time_table_periods'] + self.save + end + +end diff --git a/app/views/calendars/_form.html.slim b/app/views/calendars/_form.html.slim deleted file mode 100644 index bf9f4f3a7..000000000 --- a/app/views/calendars/_form.html.slim +++ /dev/null @@ -1,53 +0,0 @@ -= simple_form_for [@workgroup, @calendar], html: { class: 'form-horizontal', id: 'calendar_form' }, wrapper: :horizontal_form do |f| - .row - .col-lg-12 - = f.input :name - = f.input :short_name - - - if policy(@calendar).share? - .form-group.has_switch - = f.label :shared, class: 'col-sm-4 col-xs-5 control-label' - = f.input :shared, as: :boolean, checked_value: true, unchecked_value: false, label: content_tag(:span, t("#{@calendar.shared}"), class: 'switch-label', data: {checkedValue: t('true'), uncheckedValue: t('false')}), wrapper_html: { class: 'col-sm-8 col-xs-7'} - - .separator - - .row - .col-lg-12 - .subform - .nested-head - .wrapper - div - .form-group - label.control-label - = Calendar.human_attribute_name(:date) - div - - = f.simple_fields_for :date_values do |date_value| - = render 'date_value_fields', f: date_value - - .links.nested-linker - = link_to_add_association t('simple_form.labels.calendar.add_a_date'), f, :date_values, class: 'btn btn-outline-primary' - - .separator - - .row - .col-lg-12 - .subform - .nested-head - .wrapper - div - .form-group - label.control-label - = t('simple_form.labels.calendar.ranges.begin') - div - .form-group - label.control-label - = t('simple_form.labels.calendar.ranges.end') - div - - = f.simple_fields_for :periods do |period| - = render 'period_fields', f: period - .links.nested-linker - = link_to_add_association t('simple_form.labels.calendar.add_a_date_range'), f, :periods, class: 'btn btn-outline-primary' - - = f.button :submit, t('actions.submit'), class: 'btn btn-default formSubmitr', form: 'calendar_form' diff --git a/app/views/calendars/_form_advanced.html.slim b/app/views/calendars/_form_advanced.html.slim new file mode 100644 index 000000000..b4154166b --- /dev/null +++ b/app/views/calendars/_form_advanced.html.slim @@ -0,0 +1,8 @@ +#periods + += javascript_tag do + | window.actionType = "#{raw params[:action]}"; + | window.I18n = #{(I18n.backend.send(:translations)[I18n.locale].to_json).html_safe}; + | window.timetablesUrl = "#{calendar_url(@calendar).html_safe}"; + += javascript_pack_tag 'calendars/edit.js' diff --git a/app/views/calendars/_form_simple.html.slim b/app/views/calendars/_form_simple.html.slim new file mode 100644 index 000000000..2f469ada7 --- /dev/null +++ b/app/views/calendars/_form_simple.html.slim @@ -0,0 +1,56 @@ +.row + .col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-10.col-sm-offset-1 + = simple_form_for @calendar, html: { class: 'form-horizontal', id: 'calendar_form' }, wrapper: :horizontal_form do |f| + .row + .col-lg-12 + = f.input :name + = f.input :short_name + + - if policy(@calendar).share? + .form-group.has_switch + = f.label :shared, class: 'col-sm-4 col-xs-5 control-label' + = f.input :shared, as: :boolean, checked_value: true, unchecked_value: false, label: content_tag(:span, t("#{@calendar.shared}"), class: 'switch-label', data: {checkedValue: t('true'), uncheckedValue: t('false')}), wrapper_html: { class: 'col-sm-8 col-xs-7'} + + .separator + + - unless has_feature?('application_days_on_calendars') + .row + .col-lg-12 + .subform + .nested-head + .wrapper + div + .form-group + label.control-label + = Calendar.human_attribute_name(:date) + div + + = f.simple_fields_for :date_values do |date_value| + = render 'date_value_fields', f: date_value + + .links.nested-linker + = link_to_add_association t('simple_form.labels.calendar.add_a_date'), f, :date_values, class: 'btn btn-outline-primary' + + .separator + + .row + .col-lg-12 + .subform + .nested-head + .wrapper + div + .form-group + label.control-label + = t('simple_form.labels.calendar.ranges.begin') + div + .form-group + label.control-label + = t('simple_form.labels.calendar.ranges.end') + div + + = f.simple_fields_for :periods do |period| + = render 'period_fields', f: period + .links.nested-linker + = link_to_add_association t('simple_form.labels.calendar.add_a_date_range'), f, :periods, class: 'btn btn-outline-primary' + + = f.button :submit, t('actions.submit'), class: 'btn btn-default formSubmitr', form: 'calendar_form' diff --git a/app/views/calendars/edit.html.slim b/app/views/calendars/edit.html.slim index a1af5e257..79ab1f5d0 100644 --- a/app/views/calendars/edit.html.slim +++ b/app/views/calendars/edit.html.slim @@ -2,6 +2,7 @@ - page_header_content_for @calendar .page_content .container-fluid - .row - .col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-10.col-sm-offset-1 - = render 'form' + - if has_feature?('application_days_on_calendars') + = render 'form_advanced' + - else + = render 'form_simple' diff --git a/app/views/calendars/month.rabl b/app/views/calendars/month.rabl new file mode 100644 index 000000000..1584db44c --- /dev/null +++ b/app/views/calendars/month.rabl @@ -0,0 +1,9 @@ +object @calendar + +node do |tt| + { + name: I18n.l(@date, format: '%B'), + days: tt.month_inspect(@date), + day_types: %w(monday tuesday wednesday thursday friday saturday sunday).select{ |d| tt.send(d) }.map{ |d| tt.human_attribute_name(d).first(2)}.join('') + } +end diff --git a/app/views/calendars/new.html.slim b/app/views/calendars/new.html.slim index c1e084913..5657a0c55 100644 --- a/app/views/calendars/new.html.slim +++ b/app/views/calendars/new.html.slim @@ -1,6 +1,4 @@ - breadcrumb :calendars, @workgroup .page_content .container-fluid - .row - .col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-10.col-sm-offset-1 - = render 'form' + = render 'form_simple' diff --git a/app/views/calendars/show.html.slim b/app/views/calendars/show.html.slim index cd2be2bd1..cec4f66a5 100644 --- a/app/views/calendars/show.html.slim +++ b/app/views/calendars/show.html.slim @@ -11,3 +11,14 @@ 'Organisation' => resource.organisation.name, Calendar.human_attribute_name(:dates) => resource.dates.collect{|d| l(d, format: :short)}.join(', ').html_safe, Calendar.human_attribute_name(:date_ranges) => resource.periods.map{|d| t('validity_range', debut: l(d.begin, format: :short), end: l(d.end, format: :short))}.join('<br>').html_safe } + + - if has_feature?('application_days_on_calendars') + .row + .col-lg-12.mb-sm + .pagination.pull-right + = @year + .page_links + = link_to '', calendar_path(@calendar, year: (@year - 1)), class: 'previous_page' + = link_to '', calendar_path(@calendar, year: (@year + 1)), class: 'next_page' + + = render 'time_tables/show_time_table', time_table: @calendar diff --git a/app/views/calendars/show.rabl b/app/views/calendars/show.rabl new file mode 100644 index 000000000..295d97528 --- /dev/null +++ b/app/views/calendars/show.rabl @@ -0,0 +1,22 @@ +object @calendar + +attributes :id +node do |tt| + { + comment: tt.name, + time_table_bounding: tt.presenter.time_table_bounding, + day_types: %w(monday tuesday wednesday thursday friday saturday sunday).select{ |d| tt.send(d) }.map{ |d| tt.human_attribute_name(d).first(2)}.join(''), + current_month: tt.month_inspect(Date.today), + periode_range: month_periode_enum(3), + current_periode_range: Date.today.beginning_of_month, + short_id: tt.object_id, + } +end + +child(:periods, object_root: false, root: :time_table_periods) do + attributes :id, :period_start, :period_end +end + +child(:all_dates, object_root: false, root: :time_table_dates) do + attributes :id, :date, :in_out +end diff --git a/app/views/time_tables/_show_time_table.html.slim b/app/views/time_tables/_show_time_table.html.slim index ebfe9d283..102dbfad7 100644 --- a/app/views/time_tables/_show_time_table.html.slim +++ b/app/views/time_tables/_show_time_table.html.slim @@ -2,24 +2,10 @@ - (1..12).each do |month| .col-lg-3.col-md-4.col-sm-4.col-xs-6 = new_alt_calendar(year: @year, month: month, first_day_of_week: 1, calendar_title: "#{I18n.t("date.month_names")[month]}", show_today: false) do |d| - / - if @time_table.excluded_date?(d) - / - [link_to(d.mday, edit_referential_time_table_path(@referential, @time_table) ), {class: "day excluded_date"}] - - if @time_table.include_in_overlap_dates?(d) - - [link_to(d.mday, edit_referential_time_table_path(@referential, @time_table) ), {class: "day overlaped_date", title: 'Voir'}] - - elsif @time_table.include_in_dates?(d) - - [link_to(d.mday, edit_referential_time_table_path(@referential, @time_table) ), {class: "day selected_date", title: 'Voir'}] - - elsif @time_table.include_in_periods?(d) - - [link_to(d.mday, edit_referential_time_table_path(@referential, @time_table) ), {class: "day selected_period", title: 'Voir'}] - -/ .row -/ .col-lg-12 -/ / wip -/ - if @time_table.dates.where("in_out = true").present? -/ h3.time_table_dates = @time_table.human_attribute_name("dates") -/ .dates.content -/ == render "time_tables/dates" -/ -/ - if @time_table.dates.where("in_out = false").present? -/ h3.time_table_dates = @time_table.human_attribute_name("excluded_dates") -/ .excluded_dates.content -/ == render "time_tables/excluded_dates" + - edit_url = [:edit, @referential, time_table].compact + - if time_table.include_in_overlap_dates?(d) + - [link_to(d.mday, edit_url), {class: "day overlaped_date", title: 'Voir'}] + - elsif time_table.include_in_dates?(d) + - [link_to(d.mday, edit_url), {class: "day selected_date", title: 'Voir'}] + - elsif time_table.include_in_periods?(d) + - [link_to(d.mday, edit_url), {class: "day selected_period", title: 'Voir'}] diff --git a/app/views/time_tables/show.html.slim b/app/views/time_tables/show.html.slim index 6d15323f3..e038b73ca 100644 --- a/app/views/time_tables/show.html.slim +++ b/app/views/time_tables/show.html.slim @@ -24,4 +24,4 @@ = link_to '', referential_time_table_path(@referential, @time_table, year: (@year - 1)), class: 'previous_page' = link_to '', referential_time_table_path(@referential, @time_table, year: (@year + 1)), class: 'next_page' - = render 'show_time_table' + = render 'show_time_table', time_table: @time_table diff --git a/config/locales/calendars.en.yml b/config/locales/calendars.en.yml index a2110d053..c3df413af 100644 --- a/config/locales/calendars.en.yml +++ b/config/locales/calendars.en.yml @@ -69,6 +69,13 @@ en: dates: Dates shared: Shared organisation: Organisation + monday: "Monday" + tuesday: "Tuesday" + wednesday: "Wednesday" + thursday: "Thursday" + friday: "Friday" + saturday: "Saturday" + sunday: "Sunday" errors: models: calendar: diff --git a/config/locales/calendars.fr.yml b/config/locales/calendars.fr.yml index f9eaf1be5..6fd265925 100644 --- a/config/locales/calendars.fr.yml +++ b/config/locales/calendars.fr.yml @@ -69,6 +69,13 @@ fr: dates: Dates shared: Partagé organisation: Organisation + monday: "Lundi" + tuesday: "Mardi" + wednesday: "Mercredi" + thursday: "Jeudi" + friday: "Vendredi" + saturday: "Samedi" + sunday: "Dimanche" errors: models: calendar: diff --git a/config/routes.rb b/config/routes.rb index 07370ee6d..5fc39ba92 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -111,9 +111,13 @@ ChouetteIhm::Application.routes.draw do resources :companies resources :networks end + resources :workgroups do resources :calendars do get :autocomplete, on: :collection, controller: 'autocomplete_calendars' + member do + get 'month', defaults: { format: :json } + end end end @@ -121,7 +125,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] + 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/20180124124215_add_excluded_dates_to_calendars.rb b/db/migrate/20180124124215_add_excluded_dates_to_calendars.rb new file mode 100644 index 000000000..b98b50717 --- /dev/null +++ b/db/migrate/20180124124215_add_excluded_dates_to_calendars.rb @@ -0,0 +1,5 @@ +class AddExcludedDatesToCalendars < ActiveRecord::Migration + def change + add_column :calendars, :excluded_dates, :date, array: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 231bea9ba..b696cfc98 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,6 @@ # # It's strongly recommended that you check this file into your version control system. - ActiveRecord::Schema.define(version: 20180126134944) do # These are extensions that must be enabled in order to support this database diff --git a/spec/models/calendar_spec.rb b/spec/models/calendar_spec.rb index 86ce565cd..3cffd0f8a 100644 --- a/spec/models/calendar_spec.rb +++ b/spec/models/calendar_spec.rb @@ -32,7 +32,11 @@ RSpec.describe Calendar, :type => :model do describe 'validations' do it 'validates that dates and date_ranges do not overlap' do - expect(build(:calendar, dates: [Date.today], date_ranges: [Date.today..Date.tomorrow])).to_not be_valid + expect(build(:calendar, dates: [Date.today.beginning_of_week], date_ranges: [Date.today.beginning_of_week..Date.today])).to_not be_valid + end + + it 'validates that dates and date_ranges do not overlap but allow for days not in the list' do + expect(build(:calendar, dates: [Date.today.beginning_of_week], date_ranges: [Date.today.beginning_of_week..Date.today], int_day_types: Calendar::THURSDAY)).to be_valid end it 'validates that there are no duplicates in dates' do @@ -52,4 +56,108 @@ RSpec.describe Calendar, :type => :model do end end + describe "Update state" do + def calendar_to_state calendar + calendar.slice('id').tap do |item| + item['comment'] = calendar.name + item['day_types'] = "Di,Lu,Ma,Me,Je,Ve,Sa" + item['current_month'] = calendar.month_inspect(Date.today.beginning_of_month) + item['current_periode_range'] = Date.today.beginning_of_month.to_s + item['time_table_periods'] = calendar.periods.map{|p| {'id': p.id, 'period_start': p.period_start.to_s, 'period_end': p.period_end.to_s}} + end + end + + subject(:calendar){ create :calendar } + let(:state) { calendar_to_state subject } + + it 'should update time table periods association' do + period = state['time_table_periods'].first + period['period_start'] = (Date.today - 1.month).to_s + period['period_end'] = (Date.today + 1.month).to_s + + subject.state_update_periods state['time_table_periods'] + ['period_end', 'period_start'].each do |prop| + expect(subject.reload.periods.first.send(prop).to_s).to eq(period[prop]) + end + end + + it 'should create time table periods association' do + state['time_table_periods'] << { + 'id' => false, + 'period_start' => (Date.today + 1.year).to_s, + 'period_end' => (Date.today + 2.year).to_s + } + + expect { + subject.state_update_periods state['time_table_periods'] + }.to change {subject.periods.count}.by(1) + expect(state['time_table_periods'].last['id']).to eq subject.reload.periods.last.id + end + + it 'should delete time table periods association' do + state['time_table_periods'].first['deleted'] = true + expect { + subject.state_update_periods state['time_table_periods'] + }.to change {subject.periods.count}.by(-1) + end + + it 'should update name' do + state['comment'] = "Edited timetable name" + subject.state_update state + expect(subject.reload.name).to eq state['comment'] + end + + it 'should update day_types' do + state['day_types'] = "Di,Lu,Je,Ma" + subject.state_update state + expect(subject.reload.valid_days).to include(7, 1, 4, 2) + expect(subject.reload.valid_days).not_to include(3, 5, 6) + end + + it 'should delete date if date is set to neither include or excluded date' do + updated = state['current_month'].map do |day| + day['include_date'] = false if day['include_date'] + end + + expect { + subject.state_update state + }.to change {subject.dates.count}.by(-updated.compact.count) + end + + it 'should update date if date is set to excluded date' do + updated = state['current_month'].map do |day| + next unless day['include_date'] + day['include_date'] = false + day['excluded_date'] = true + end + + subject.state_update state + expect(subject.reload.excluded_days.count).to eq (updated.compact.count) + end + + it 'should create new include date' do + day = state['current_month'].find{|d| !d['excluded_date'] && !d['include_date'] } + date = Date.parse(day['date']) + day['include_date'] = true + expect(subject.included_days).not_to include(date) + + expect { + subject.state_update state + }.to change {subject.dates.count}.by(1) + expect(subject.reload.included_days).to include(date) + end + + it 'should create new exclude date' do + day = state['current_month'].find{|d| !d['excluded_date'] && !d['include_date']} + date = Date.parse(day['date']) + day['excluded_date'] = true + expect(subject.excluded_days).not_to include(date) + + expect { + subject.state_update state + }.to change {subject.all_dates.count}.by(1) + expect(subject.reload.excluded_days).to include(date) + end + end + end |
