%= javascript_include_tag("calendar", plugin: :redmine_qbo_calendar) %>
<%
# Check if toggles are active via query params
only_appointments = params[:only_appointments] == '1'
hide_past_weeks = params[:hide_past_weeks] == '1'
# Current date context
today = User.current.today
is_current_month = (calendar.instance_variable_get(:@month) == today.month && calendar.instance_variable_get(:@year) == today.year)
# Get Redmine's configured non-working week days (e.g., ["6", "7"] for Sat/Sun)
non_working_days = Setting.non_working_week_days || []
all_holidays = Holiday.all
# Safely collect all unique CustomerAppointments across the entire calendar date range
all_appointments = (calendar.startdt..calendar.enddt).flat_map { |date|
calendar.events_on(date)
}.select { |e| e.is_a?(CustomerAppointment) }.uniq
# Lambda to determine if an appointment should render on a specific calendar day
appointment_occurs_on = lambda do |appointment, current_day|
app_datetime = appointment.respond_to?(:date) && appointment.date.present? ? appointment.date : appointment.start_date
return false if app_datetime.nil?
start_date = app_datetime.to_date
target_date = current_day.to_date
return false if target_date < start_date
# Skip rendering on Redmine standard non-working days
return false if non_working_days.include?(target_date.cwday.to_s)
# Skip rendering on custom non-working days (Holidays)
return false if all_holidays.any? { |h| h.occurs_on?(target_date) }
duration = [appointment.duration.to_i, 1].max
date_cursor = start_date
working_days_count = 0
while date_cursor <= target_date
is_standard_weekend = non_working_days.include?(date_cursor.cwday.to_s)
is_custom_holiday = all_holidays.any? { |h| h.occurs_on?(date_cursor) }
# Only increment duration count if it's a standard working day AND not a holiday
unless is_standard_weekend || is_custom_holiday
working_days_count += 1
end
if date_cursor == target_date
return working_days_count <= duration
end
date_cursor += 1 # Pure Ruby Date increment stays a Date object
end
false
end
%>
<%# Calendar Status Legend Key %>
<%
# Collect status keys from CustomerAppointment enum
statuses = CustomerAppointment::STATUSES
%>
<% if statuses.any? %>
<%= l(:label_legend, default: 'Legend') %>:
<% statuses.each do |status_key| %>
<%= status_key.humanize %>
<% end %>
<% end %>
<% calendar.format_month.each_slice(7) do |week| %>
<%# Skip week if "hide past weeks" is active and every day in the week is in the past %>
<% next if hide_past_weeks && week.last < User.current.today %>
<% week.each do |day| %>
<%
is_today = (day == User.current.today)
# Find the specific holiday for this day (if any)
current_holiday = all_holidays.find { |h| h.occurs_on?(day) }
is_custom_holiday = current_holiday.present?
is_standard_weekend = non_working_days.include?(day.cwday.to_s)
# Filter out standard events if "only appointments" toggle is active
standard_events = only_appointments ? [] : calendar.events_on(day).reject { |e| e.is_a?(CustomerAppointment) }
# Appointments matching duration on this working day
matching_appointments = all_appointments.select { |app| appointment_occurs_on.call(app, day) }
# Sort events chronologically by time of day
day_events = (standard_events + matching_appointments).sort_by do |event|
if event.is_a?(CustomerAppointment) && event.date.present?
event.date.strftime('%H:%M:%S')
elsif event.respond_to?(:start_date) && event.start_date.present?
event.start_date.to_time.strftime('%H:%M:%S')
else
'00:00:00'
end
end
# Sum estimated hours for Issues and CustomerAppointments
total_hours = day_events.sum do |event|
if event.is_a?(Issue)
event.estimated_hours.to_f
elsif event.is_a?(CustomerAppointment)
if event.respond_to?(:estimated_hours) && event.estimated_hours.present?
event.estimated_hours.to_f
else
event.duration.to_f
end
else
0.0
end
end
%>
<%# Added 'week-end' class and background color styling for custom holidays to mimic weekends %>
>
style="text-decoration: none; color: inherit;">
<%= day.day %>(<%= abbr_day_name(day.cwday) %>)
<%# Only show ADD button if it's in the future, NOT a weekend, and NOT a custom holiday %>
<% if day >= User.current.today && !is_standard_weekend && !is_custom_holiday %>
<%= link_to sprite_icon('add'),
new_customer_appointment_path(start_date: day),
class: 'icon-only icon-add',
title: l(:label_appointment_new, default: 'New Appointment') %>
<% end %>
<% if total_hours > 0 %>
<%= total_hours.round(1) %>h
<% end %>
<%# Render Holiday Name Badge if it's a holiday %>
<% if is_custom_holiday %>