Module: Crm::TimeOffsHelper
- Includes:
- TimeOffPlannedWorkDayHelper
- Defined in:
- app/helpers/crm/time_offs_helper.rb
Constant Summary collapse
- MANAGER_DEPARTMENT_ICON_BY_NAME =
Canonical department label (lowercase) -> Font Awesome icon name for fa_icon (sharp regular).
Used for exact match first, then substring scan (see MANAGER_DEPARTMENT_ICON_FRAGMENTS).
Includes administration / admin / no department; add new departments here only. { 'it' => 'building', 'information technology' => 'building', 'engineering' => 'screwdriver-wrench', 'support' => 'headset', 'customer support' => 'headset', 'sales' => 'handshake', 'marketing' => 'bullhorn', 'finance' => 'coins', 'accounting' => 'file-invoice-dollar', 'hr' => 'people-group', 'human resources' => 'people-group', 'operations' => 'gears', 'warehouse' => 'warehouse', 'shipping' => 'truck', 'logistics' => 'truck-fast', 'legal' => 'scale-balanced', 'executive' => 'briefcase', 'management' => 'user-tie', 'tech 24x7' => 'clock', 'production' => 'industry', 'quality' => 'clipboard-check', 'purchasing' => 'cart-shopping', 'buying' => 'cart-shopping', 'administration' => 'shield-halved', 'admin' => 'shield-halved', 'no department' => 'users' }.freeze
- MANAGER_DEPARTMENT_ICON_FRAGMENT_EXCLUDE_KEYS =
Keys excluded from substring matching: +include?+ would false-positive (e.g. "digital" for "it").
Those labels are still handled by MANAGER_DEPARTMENT_ICON_BY_NAME (exact) or /\bit\b/ / /\bhr\b/ below. %w[it hr].freeze
- MANAGER_DEPARTMENT_ICON_FRAGMENTS =
Derived from MANAGER_DEPARTMENT_ICON_BY_NAME: longest keys first so "customer support" beats "support".
MANAGER_DEPARTMENT_ICON_BY_NAME .reject { |k, _| MANAGER_DEPARTMENT_ICON_FRAGMENT_EXCLUDE_KEYS.include?(k) } .sort_by { |k, _| [-k.length, k] } .freeze
- MANAGER_DEPARTMENT_ICON_FALLBACKS =
%w[ building briefcase users industry star diagram-project ].freeze
- PENDING_BADGE_VARIANT_CLASSES =
Bootstrap background utilities for +pending_badge+ (+variant+ must be a known key).
{ primary: 'bg-primary', warning: 'bg-warning text-dark' }.freeze
- TIME_OFF_TYPE_DISPLAY_TEXT_CLASS =
Maps time-off row type (TimeOffType#name or the literal "holiday") to Bootstrap 5 text utilities.
Output is always from a fixed allowlist — never interpolates DB color strings into markup. { 'holiday' => 'text-warning', 'Vacation' => 'text-success', 'Birthday' => 'text-warning', 'Unpaid leave' => 'text-secondary', 'Community Service' => 'text-success', 'Short-Term Disability (STD)' => 'text-primary', 'Jury Duty' => 'text-info', 'Bereavement' => 'text-secondary', 'Banked Time' => 'text-primary' }.freeze
- FALLBACK_TEXT_CLASSES =
%w[ text-primary text-secondary text-success text-danger text-warning text-info text-body-secondary ].freeze
- TIME_OFF_TYPE_UNIT_OPTIONS =
[['Hours', 'hour'], ['Days', 'day']].freeze
- TIME_OFF_TYPE_ICON_SUGGESTIONS =
Kebab-case Font Awesome icon names (no
fa-prefix) for Tom Select suggestions. %w[ umbrella-beach calendar-days calendar-xmark calendar-check sun snowflake heart-pulse bandage person-falling plane car gift baby cross house-chimney clock mug-hot briefcase beach-ball ballot-check star sparkles martini-glass cake-candles hands-praying user-doctor stethoscope mask-face thermometer ].freeze
- TIME_OFF_TYPE_COLOR_PICKER_DEFAULT =
Default swatch for
<input type="color">when none is set or value is not a hex the input accepts. '#913120'- TIME_OFF_POLICY_ACCRUAL_FREQUENCY_OPTIONS =
Values match existing policy rows (e.g. seeds) and Employee#accrual handling uses .downcase.
%w[Weekly Monthly Yearly].map { |w| [w, w] }.freeze
- TIME_OFF_POLICY_WEEKLY_DAY_OPTIONS =
Date::DAYNAMES.each_with_index.map { |day, index| [day, index] }.freeze
- TIME_OFF_POLICY_ANNUAL_ACCRUAL_TIMING_OPTIONS =
[ ['Beginning of Year', 'beginning_of_year'], ['End of Year', 'end_of_year'] ].freeze
Instance Method Summary collapse
-
#manager_department_icon(department_label) ⇒ Object
Icon for a manager dashboard department heading (decorative; label remains plain text).
-
#manager_schedule_grid_cell_kind(row_data, slot, slot_end) ⇒ Object
Row hash from Crm::TimeOffsController#setup_manager_dashboard_data (+ :time_off_request_date).
-
#pending_badge(count, variant: :primary, title: '', extra_classes: '') ⇒ Object
Pill badge for manager employee chip pending counts; returns nil when +count+ is not positive.
- #time_off_policy_parent_options(record) ⇒ Object
- #time_off_type_color_class(type) ⇒ Object
-
#time_off_type_color_picker_value(record) ⇒ Object
HTML color input expects #rrggbb; fall back when DB has named colors or invalid values.
-
#time_off_type_icon_tom_select_collection(record) ⇒ Object
Pairs [label, value] for icon Tom Select; includes current value if not in the suggestion list.
-
#vacation_summary_bar_chart_data(summary) ⇒ Object
Series for the Summary tab bar chart (horizontal bars, same metrics as the KPI cards).
-
#vacation_summary_hours_display(value) ⇒ Object
Matches TimeOffBalanceCalculator#format_number for vacation_summary raw values.
Methods included from TimeOffPlannedWorkDayHelper
#time_off_planned_work_day_summary
Instance Method Details
#manager_department_icon(department_label) ⇒ Object
Icon for a manager dashboard department heading (decorative; label remains plain text).
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'app/helpers/crm/time_offs_helper.rb', line 59 def manager_department_icon(department_label) key = department_label.to_s.strip.downcase return 'users' if key.blank? icon = MANAGER_DEPARTMENT_ICON_BY_NAME[key] return icon if icon MANAGER_DEPARTMENT_ICON_FRAGMENTS.each do |fragment, frag_icon| return frag_icon if key.include?(fragment) end return 'building' if /\bit\b/.match?(key) return 'people-group' if /\bhr\b/.match?(key) idx = key.bytes.sum % MANAGER_DEPARTMENT_ICON_FALLBACKS.size MANAGER_DEPARTMENT_ICON_FALLBACKS[idx] end |
#manager_schedule_grid_cell_kind(row_data, slot, slot_end) ⇒ Object
Row hash from Crm::TimeOffsController#setup_manager_dashboard_data (+ :time_off_request_date).
232 233 234 235 236 237 238 239 240 |
# File 'app/helpers/crm/time_offs_helper.rb', line 232 def manager_schedule_grid_cell_kind(row_data, slot, slot_end) Crm::ManagerScheduleGridSlot.cell_kind( slot: slot, slot_end: slot_end, schedule_day: row_data[:schedule_day], time_off_request_date: row_data[:time_off_request_date], ongoing_time_off_request: row_data[:ongoing_request] ) end |
#pending_badge(count, variant: :primary, title: '', extra_classes: '') ⇒ Object
Pill badge for manager employee chip pending counts; returns nil when +count+ is not positive.
84 85 86 87 88 89 90 91 92 93 |
# File 'app/helpers/crm/time_offs_helper.rb', line 84 def pending_badge(count, variant: :primary, title: '', extra_classes: '') return nil unless count.respond_to?(:positive?) && count.positive? variant_classes = PENDING_BADGE_VARIANT_CLASSES.fetch(variant.to_sym) class_parts = ['badge', 'rounded-pill', variant_classes] class_parts.concat(extra_classes.to_s.split) if extra_classes.present? attrs = { class: class_parts.join(' ') } attrs[:title] = title if title.present? tag.span(count.to_s, **attrs) end |
#time_off_policy_parent_options(record) ⇒ Object
225 226 227 228 229 |
# File 'app/helpers/crm/time_offs_helper.rb', line 225 def (record) scope = TimeOffPolicy.order(:name) scope = scope.where.not(id: record.id) if record.persisted? scope.pluck(:name, :id) end |
#time_off_type_color_class(type) ⇒ Object
113 114 115 116 117 118 119 120 |
# File 'app/helpers/crm/time_offs_helper.rb', line 113 def time_off_type_color_class(type) key = type.to_s return TIME_OFF_TYPE_DISPLAY_TEXT_CLASS[key] if TIME_OFF_TYPE_DISPLAY_TEXT_CLASS.key?(key) # Unknown type names (e.g. newly added in admin): pick a stable class from the allowlist only. idx = key.bytes.sum % FALLBACK_TEXT_CLASSES.size FALLBACK_TEXT_CLASSES[idx] end |
#time_off_type_color_picker_value(record) ⇒ Object
HTML color input expects #rrggbb; fall back when DB has named colors or invalid values.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'app/helpers/crm/time_offs_helper.rb', line 188 def time_off_type_color_picker_value(record) c = record.color.to_s.strip return TIME_OFF_TYPE_COLOR_PICKER_DEFAULT if c.blank? if c.match?(/\A#[0-9a-fA-F]{6}\z/i) c elsif c.match?(/\A#[0-9a-fA-F]{3}\z/i) "##{c[1]}#{c[1]}#{c[2]}#{c[2]}#{c[3]}#{c[3]}" elsif c.match?(/\A#[0-9a-fA-F]{8}\z/i) c[0..6] else TIME_OFF_TYPE_COLOR_PICKER_DEFAULT end end |
#time_off_type_icon_tom_select_collection(record) ⇒ Object
Pairs [label, value] for icon Tom Select; includes current value if not in the suggestion list.
204 205 206 207 208 209 210 211 212 |
# File 'app/helpers/crm/time_offs_helper.rb', line 204 def time_off_type_icon_tom_select_collection(record) normalized = record.icon.to_s.strip.sub(/\Afa-/, '').tr('_', '-').downcase pairs = TIME_OFF_TYPE_ICON_SUGGESTIONS.map { |i| [i.tr('-', ' ').titleize, i] } if normalized.present? && TIME_OFF_TYPE_ICON_SUGGESTIONS.exclude?(normalized) pairs = pairs.dup pairs.unshift(["#{normalized.tr('-', ' ').titleize} (current)", normalized]) end pairs end |
#vacation_summary_bar_chart_data(summary) ⇒ Object
Series for the Summary tab bar chart (horizontal bars, same metrics as the KPI cards).
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'app/helpers/crm/time_offs_helper.rb', line 132 def (summary) return if summary.blank? rows = [ { label: 'Used time as of today', value: summary[:used_until_today].to_f, icon: 'fa-regular fa-clock', fill_class: 'bg-danger' }, { label: 'Available time as of today', value: summary[:available_today].to_f, icon: 'fa-regular fa-circle-check', fill_class: 'bg-success' }, { label: 'Approved planned until year-end', value: summary[:approved_between_today_and_eoy].to_f, icon: 'fa-regular fa-calendar-check', fill_class: 'bg-warning' }, { label: 'Pending approval until year-end', value: summary[:pending_between_today_and_eoy].to_f, icon: 'fa-regular fa-hourglass-half', fill_class: 'bg-secondary' }, { label: 'Available at year-end', value: summary[:available_eoy].to_f, icon: 'fa-regular fa-calendar-xmark', fill_class: 'bg-primary' } ] max_value = rows.map { |r| r[:value] }.max.to_f max_value = 1.0 if max_value <= 0 { rows:, max_value: } end |
#vacation_summary_hours_display(value) ⇒ Object
Matches TimeOffBalanceCalculator#format_number for vacation_summary raw values.
123 124 125 126 127 128 129 |
# File 'app/helpers/crm/time_offs_helper.rb', line 123 def vacation_summary_hours_display(value) return 0 if value.blank? n = value.to_f rounded = n.round(2) (rounded % 1).zero? ? rounded.to_i : rounded end |