Implemented holiday selector for calendars.

This commit is contained in:
Martin Edenhofer 2015-09-25 09:21:55 +02:00
parent b44dbe371f
commit fdea787b2f
7 changed files with 178 additions and 90 deletions

View file

@ -1,7 +1,7 @@
# coffeelint: disable=camel_case_classes
class App.UiElement.holiday_selector
@render: (attribute, params) ->
console.log('aa', attribute)
days = {}
if attribute.value
days = attribute.value
@ -11,3 +11,64 @@ class App.UiElement.holiday_selector
days_new[day] = days[day]
item = $( App.view('calendar/holiday_selector')( attribute: attribute, days: days_new ) )
# add date picker
attributeDatepicket =
name: "#{attribute.name}_date"
disable_feature: true
datePicker = App.UiElement.date.render(attributeDatepicket)
item.find('.js-datePicker').html(datePicker)
# set active/inactive of date
item.find('.js-active').bind('click', (e) ->
active = $(e.target).prop('checked')
row = $(e.target).closest('tr')
input = $(e.target).closest('tr').find('.js-description')
if !active
row.addClass('is-inactive')
input.prop('readonly', true)
input.addClass('is-disabled')
else
row.removeClass('is-inactive')
input.prop('readonly', false)
input.removeClass('is-disabled')
)
# remove date
item.find('.js-remove').bind('click', (e) ->
$(e.target).closest('tr').remove()
)
# catch enter / apply add
item.find('.js-summary').bind( 'keydown', (e) ->
return if e.which isnt 13
e.preventDefault()
item.find('.js-add').click()
)
# add date
item.find('.js-add').bind('click', (e) ->
date = $(e.target).closest('tr').find('[name="{date}public_holidays_date"]').val()
return if !date
summary = $(e.target).closest('tr').find('.js-summary').val()
return if !summary
# check if entry already exists
exists = item.find("[data-date=#{date}]").get(0)
return if exists
# reset form input
$(e.target).closest('tr').find('.js-summary').val('')
# place new element
template = item.find('.js-placeholder').clone()
template.removeClass('hidden').removeClass('js-placeholder')
template.attr('data-date', date)
template.find('.js-date').html(App.i18n.translateDate(date))
template.find('.js-active').attr('name', "{boolean}public_holidays::#{date}::active")
template.find('.js-summary').attr('name', "public_holidays::#{date}::summary")
template.find('.js-summary').val(summary)
item.find('.js-placeholder').before(template)
)
item

View file

@ -1,36 +1,48 @@
# coffeelint: disable=camel_case_classes
class App.UiElement.ical_feed extends App.UiElement.ApplicationUiElement
@render: (attribute, params) ->
console.log('A', attribute)
item = $( '<div>' + App.view('generic/input')( attribute: attribute ) + '</div>' )
ical_feeds = App.Config.get('ical_feeds')
ical_feeds = App.Config.get('ical_feeds') || {}
item = $( App.view('generic/ical_feed')( attribute: attribute, ical_feeds: ical_feeds ) )
if !_.isEmpty(ical_feeds)
attribute_ical =
options: ical_feeds
tag: 'searchable_select'
placeholder: App.i18n.translateInline('Search public ical feed...')
updateCheckList = ->
return if item.find('.js-checkList').prop('checked')
return if !item.find('.js-list').val()
item.find('.js-checkList').prop('checked', true)
item.find('.js-checkManual').prop('checked', false)
# build options list based on config
@getConfigOptionList( attribute_ical )
updateCheckManual = ->
return if item.find('.js-checkManual').prop('checked')
item.find('.js-checkList').prop('checked', false)
item.find('.js-checkManual').prop('checked', true)
# add null selection if needed
@addNullOption( attribute_ical )
updateShadow = (selected) ->
if !selected
selected = item.find('.js-check:checked').attr('value')
if selected is 'manual'
item.find('.js-shadow').val( item.find('.js-manual').val() )
else
item.find('.js-shadow').val( item.find('.js-list').val() )
# sort attribute.options
@sortOptions( attribute_ical )
# set inital state
if ical_feeds[attribute.value]
updateCheckList()
else
updateCheckManual()
item.find('.js-manual').val(attribute.value)
# finde selected/checked item of list
@selectedOptions( attribute_ical )
item.find('.js-check').bind('change', ->
updateShadow()
)
templateSelections = App.UiElement.searchable_select.render(attribute_ical)
item.find('.js-list').bind('click change', ->
updateCheckList()
updateShadow('list')
)
templateSelections.find('.js-shadow').bind('change', (e) ->
val = $(e.target).val()
if val
item.find("[name=#{attribute.name}]").val(val)
)
item.append(templateSelections)
item.find('.js-manual').bind('keyup focus blur', ->
updateCheckManual()
updateShadow('manual')
)
item

View file

@ -58,7 +58,8 @@ class Index extends App.ControllerContent
for day in keys
itemTime = new Date( Date.parse( "#{day}T00:00:00Z" ) )
if itemTime < till && itemTime > from
public_holidays_preview[day] = calendar.public_holidays[day]
if calendar.public_holidays[day] && calendar.public_holidays[day].active
public_holidays_preview[day] = calendar.public_holidays[day]
calendar.public_holidays_preview = public_holidays_preview
# show description button, only if content exists

View file

@ -1,5 +1,5 @@
class App.Calendar extends App.Model
@configure 'Calendar', 'name', 'timezone', 'default', 'business_hours', 'ical_url', 'public_holidays'
@configure 'Calendar', 'name', 'timezone', 'default', 'business_hours', 'ical_url', 'public_holidays', 'note'
@extend Spine.Model.Ajax
@url: @apiPath + '/calendars'
@ -7,8 +7,8 @@ class App.Calendar extends App.Model
{ name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, null: false },
{ name: 'timezone', display: 'Timezone', tag: 'timezone', null: false }
{ name: 'business_hours', display: 'Business Hours', tag: 'business_hours', null: true }
{ name: 'ical_url', display: 'Public Holidays iCal Feed', tag: 'ical_feed', placeholder: 'http://example.com/public_holidays.ical', null: true }
{ name: 'public_holidays',display: 'Public Holidays', tag: 'holiday_selector', null: true }
{ name: 'ical_url', display: 'Holidays iCalendar Feed', tag: 'ical_feed', placeholder: 'http://example.com/public_holidays.ical', null: true }
{ name: 'public_holidays',display: 'Holidays', tag: 'holiday_selector', null: true }
{ name: 'note', display: 'Note', tag: 'textarea', limit: 250, null: true },
{ name: 'created_by_id', display: 'Created by', relation: 'User', readonly: 1 },
{ name: 'created_at', display: 'Created', tag: 'datetime', readonly: 1 },

View file

@ -8,59 +8,49 @@
</thead>
<tbody>
<% for day, meta of @days: %>
<tr <% if !meta.active: %>class="is-inactive"<% end %>>
<tr <% if !meta.active: %>class="is-inactive"<% end %> data-date="<%= day %>">
<td>
<label class="checkbox-replacement">
<input type="checkbox" checked>
<input type="checkbox" <% if meta.active: %>checked<% end %> class="js-active" name="{boolean}public_holidays::<%= day %>::active" value="true">
<%- @Icon('checkbox', 'icon-unchecked') %>
<%- @Icon('checkbox-checked', 'icon-checked') %>
</label>
<td><%- @Tdate(day) %>
<td><%= meta.summary %>
<td><input class="form-control form-control--small js-description <% if !meta.active: %>is-disabled<% end %>" type="text" name="public_holidays::<%= day %>::summary" value="<%= meta.summary %>" required/>
<td>
<div class="settings-list-rowControls">
<div class="btn btn--text js-remove">
<%- @Icon('trash') %> <%- @T('Remove') %>
</div>
<% if !meta.feed: %>
<div class="btn btn--text js-remove">
<%- @Icon('trash') %> <%- @T('Remove') %>
</div>
<% end %>
</div>
<% end %>
<tr>
<tr class="hidden js-placeholder" data-date="">
<td>
<label class="checkbox-replacement">
<input type="checkbox" checked>
<input type="checkbox" checked class="js-active" name="" value="true">
<%- @Icon('checkbox', 'icon-unchecked') %>
<%- @Icon('checkbox-checked', 'icon-checked') %>
</label>
<td><%- @Tdate('2015-12-25') %>
<td>Some Description
<td>
<div class="settings-list-rowControls">
<div class="btn btn--text js-remove">
<%- @Icon('trash') %> <%- @T('Remove') %>
</div>
</div>
<tr class="is-inactive">
<td>
<label class="checkbox-replacement">
<input type="checkbox">
<%- @Icon('checkbox', 'icon-unchecked') %>
<%- @Icon('checkbox-checked', 'icon-checked') %>
</label>
<td><%- @Tdate('2015-12-26') %>
<td>Some Description
<td class="js-date">
<td><input class="form-control form-control--small js-summary" type="text" name="" value="<%= meta.summary %>" required/>
<td>
<div class="settings-list-rowControls">
<div class="btn btn--text js-remove">
<%- @Icon('trash') %> <%- @T('Remove') %>
</div>
</div>
<tr class="settings-list-controlRow">
<td>
<td class="js-datePicker">
<!-- not supported right now by ff
<input class="form-control form-control--small" type="date" placeholder="<%- @T('Date') %>"/>
-->
<td>
<!-- Hallo Martin! Allow to add by pressing enter! -->
<input class="form-control form-control--small" type="date" name="public_holidays_date" placeholder="<%- @T('Date') %>"/>
<td>
<input class="form-control form-control--small" type="text" name="public_holidays_description" placeholder="<%- @T('Description') %>"/>
<input class="form-control form-control--small js-summary" type="text" placeholder="<%- @T('Description') %>"/>
<td>
<div class="btn btn--text js-add">
<%- @Icon('plus-small') %> <%- @T('Add') %>

View file

@ -1161,7 +1161,7 @@ input.time.time--12 {
display: none;
}
.form-control[disabled] {
.form-control[disabled], .form-control.is-disabled {
cursor: not-allowed;
background-color: #fff;
color: #d5d5d5;

View file

@ -4,8 +4,8 @@ class Calendar < ApplicationModel
store :business_hours
store :public_holidays
before_create :fetch_ical
before_update :fetch_ical
before_create :validate_public_holidays, :fetch_ical
before_update :validate_public_holidays, :fetch_ical
after_create :sync_default, :min_one_check
after_update :sync_default, :min_one_check
after_destroy :min_one_check
@ -71,14 +71,14 @@ returns
def self.ical_feeds
gfeeds = {
'Australian' => 'en.australian',
'Austrian' => 'de.austrian',
'Australia' => 'en.australian',
'Austria' => 'de.austrian',
'Argentina' => 'en.ar',
'Bahamas' => 'en.bs',
'Belarus' => 'en.by',
'Brazilian' => 'en.brazilian',
'Brazil' => 'en.brazilian',
'Bulgaria' => 'en.bulgarian',
'Canadian' => 'en.canadian',
'Canada' => 'en.canadian',
'China' => 'en.china',
'Chile' => 'en.cl',
'Costa Rica' => 'en.cr',
@ -87,47 +87,44 @@ returns
'Cuba' => 'en.cu',
'Cyprus' => 'de.cy',
'Switzerland' => 'de.ch',
'Christian' => 'en.christian',
'Danish' => 'da.danish',
'Dutch' => 'nl.dutch',
'Denmark' => 'da.danish',
'Netherlands' => 'nl.dutch',
'Egypt' => 'en.eg',
'Ethiopia' => 'en.et',
'Ecuador' => 'en.ec',
'Estonia' => 'en.ee',
'Finnish' => 'en.finnish',
'French' => 'en.french',
'German' => 'de.german',
'Greek' => 'en.greek',
'Finland' => 'en.finnish',
'France' => 'en.french',
'Germany' => 'de.german',
'Greece' => 'en.greek',
'Ghana' => 'en.gh',
'Hong Kong' => 'en.hong_kong',
'Haiti' => 'en.ht',
'Hungary' => 'en.hungarian',
'Indian' => 'en.indian',
'Indonesian' => 'en.indonesian',
'Iranian' => 'en.ir',
'Irish' => 'en.irish',
'Islamic' => 'en.islamic',
'Italian' => 'it.italian',
'India' => 'en.indian',
'Indonesia' => 'en.indonesian',
'Iran' => 'en.ir',
'Ireland' => 'en.irish',
'Italy' => 'it.italian',
'Israel' => 'en.jewish',
'Japanese' => 'en.japanese',
'Jewish' => 'en.jewish',
'Japan' => 'en.japanese',
'Kuwait' => 'en.kw',
'Latvia' => 'en.latvian',
'Liechtenstein' => 'en.li',
'Lithuania' => 'en.lithuanian',
'Luxembourg' => 'en.lu',
'Malaysian' => 'en.malaysia',
'Mexican' => 'en.mexican',
'Malaysia' => 'en.malaysia',
'Mexico' => 'en.mexican',
'Morocco' => 'en.ma',
'Mauritius' => 'en.mu',
'Moldova' => 'en.md',
'New Zealand' => 'en.new_zealand',
'Norwegian' => 'en.norwegian',
'Norway' => 'en.norwegian',
'Philippines' => 'en.philippines',
'Polish' => 'en.polish',
'Portuguese' => 'en.portuguese',
'Poland' => 'en.polish',
'Portugal' => 'en.portuguese',
'Pakistan' => 'en.pk',
'Russian' => 'en.russian',
'Russia' => 'en.russian',
'Senegal' => 'en.sn',
'Singapore' => 'en.singapore',
'South Africa' => 'en.sa',
@ -136,7 +133,7 @@ returns
'Slovakia' => 'en.slovak',
'Serbia' => 'en.rs',
'Slovenia' => 'en.slovenian',
'Swedish' => 'en.swedish',
'Sweden' => 'en.swedish',
'Taiwan' => 'en.taiwan',
'Thai' => 'en.th',
'Turkey' => 'en.turkish',
@ -144,12 +141,12 @@ returns
'US' => 'en.usa',
'Ukraine' => 'en.ukrainian',
'Uruguay' => 'en.uy',
'Vietnamese' => 'en.vietnamese',
'Vietnam' => 'en.vietnamese',
'Venezuela' => 'en.ve',
}
all_feeds = {}
gfeeds.each {|key, name|
all_feeds["http://www.google.com/calendar/ical/#{name}%23holiday%40group.v.calendar.google.com/public/basic.ics"] = "#{key} - Holidays"
all_feeds["http://www.google.com/calendar/ical/#{name}%23holiday%40group.v.calendar.google.com/public/basic.ics"] = key
}
all_feeds
end
@ -218,6 +215,15 @@ returns
if !public_holidays
self.public_holidays = {}
end
# remove old ical entries if feed has changed
public_holidays.each {|day, meta|
next if !public_holidays[day]['feed']
next if meta['feed'] == Digest::MD5.hexdigest(ical_url)
public_holidays.delete(day)
}
# sync new ical feed dates
events.each {|day, summary|
if !public_holidays[day]
public_holidays[day] = {}
@ -230,6 +236,7 @@ returns
public_holidays[day] = {
active: true,
summary: summary,
feed: Digest::MD5.hexdigest(ical_url)
}
}
self.last_log = nil
@ -315,4 +322,21 @@ returns
def fetch_ical
sync(true)
end
# validate format of public holidays
def validate_public_holidays
# fillup feed info
public_holidays.each {|day, meta|
if public_holidays_was && public_holidays_was[day] && public_holidays_was[day]['feed']
meta['feed'] = public_holidays_was[day]['feed']
end
if meta['active']
meta['active'] = true
else
meta['active'] = false
end
}
end
end