Implemented holiday selector for calendars.
This commit is contained in:
parent
b44dbe371f
commit
fdea787b2f
7 changed files with 178 additions and 90 deletions
|
@ -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
|
||||
|
|
|
@ -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 )
|
||||
|
||||
templateSelections = App.UiElement.searchable_select.render(attribute_ical)
|
||||
|
||||
templateSelections.find('.js-shadow').bind('change', (e) ->
|
||||
val = $(e.target).val()
|
||||
if val
|
||||
item.find("[name=#{attribute.name}]").val(val)
|
||||
item.find('.js-check').bind('change', ->
|
||||
updateShadow()
|
||||
)
|
||||
|
||||
item.find('.js-list').bind('click change', ->
|
||||
updateCheckList()
|
||||
updateShadow('list')
|
||||
)
|
||||
|
||||
item.find('.js-manual').bind('keyup focus blur', ->
|
||||
updateCheckManual()
|
||||
updateShadow('manual')
|
||||
)
|
||||
item.append(templateSelections)
|
||||
|
||||
item
|
|
@ -58,6 +58,7 @@ class Index extends App.ControllerContent
|
|||
for day in keys
|
||||
itemTime = new Date( Date.parse( "#{day}T00:00:00Z" ) )
|
||||
if itemTime < till && itemTime > from
|
||||
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
|
||||
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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">
|
||||
<% if !meta.feed: %>
|
||||
<div class="btn btn--text js-remove">
|
||||
<%- @Icon('trash') %> <%- @T('Remove') %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<tr>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<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') %>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue