hr_lite 0.35.0 → 0.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/app/assets/stylesheets/hr_lite/hr_lite.css +13 -1
- data/app/controllers/hr_lite/admin/work_modes_controller.rb +1 -1
- data/app/controllers/hr_lite/work_modes_controller.rb +1 -1
- data/app/services/hr_lite/work_mode_switch.rb +31 -6
- data/app/views/hr_lite/admin/attendances/show.html.erb +1 -1
- data/app/views/hr_lite/attendance/_duration_chips.html.erb +16 -0
- data/app/views/hr_lite/attendance/_punch_card.html.erb +9 -6
- data/app/views/hr_lite/attendance/_work_mode.html.erb +26 -11
- data/lib/hr_lite/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 91b052360d5658195be8a557e080ddcaea5660de77265ff6cb4d030b879f23a8
|
|
4
|
+
data.tar.gz: 32a1e1624df566187143af64b995b0af4a2db0c44463ff7600d0f6d7ed0f2e32
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0caced16fcf281925ba4ec901631dcffcd20e07aad5124781c67751298f1dfde737eb9ca60eaf4f3c100d47129a76a83bfb2ff93bd72d21db6de2834319dbc57
|
|
7
|
+
data.tar.gz: 8002305bba4b795098b2499a6765809a2a1aebf3c281fe715e7fc72f349ef3630cc653f2abbda98c32d03df4f5c223cbd4bf17e4ddb8b8404085aaa080a80f40
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.36.0] - 2026-10-13
|
|
11
|
+
|
|
12
|
+
- Pick how long a mode lasts: Busy, Meeting and Out of office offer 15 min, 30 min, 1 h, 2 h, 4 h or 1 day (30 min preselected); Travelling and Client meeting also offer "Until I change it" (the default). Lunch and short break offer only lengths within their company limit — lunch 15 or 30 min by default — and one tap still takes the full limit; a longer break is refused. Every mode ends on its own at the picked time; the longest is a day. The choices are real radio buttons shown as 44px chips with 8px gaps.
|
|
13
|
+
|
|
14
|
+
## [0.35.1] - 2026-10-13
|
|
15
|
+
|
|
16
|
+
- Spacing: forms, buttons and expandable sections stacked in a card no longer touch (only form fields carried spacing, so "Set a status" sat on "Change my mode", "Save status" on "Check in", "Out of office" on "Check out"). One card-level rule gives them a 12px gap. The emoji grid, lead picker, check-in badges, punch-card links (now wrapping as whole items, no dangling dots) and the admin month's In/Out map links all keep at least the 8px gap between touch targets. Measured at 360 px and 1280 px across Home, Attendance, WFH, WFH locations, My work time, Team and the HR work-mode, WFH, locations, work-time, attendance, holidays and employee screens: no stacked targets closer than 8px, no horizontal scroll.
|
|
17
|
+
|
|
10
18
|
## [0.35.0] - 2026-10-13
|
|
11
19
|
|
|
12
20
|
- Today's check-ins on Home: everyone in today, split into on time, late and not in yet (leave, holidays and weekends left out). "On time" uses each person's own slot plus their HR buffer and the grace minutes, the same rule as the late penalty. Hover or tap a person for their check-in, how late, check-out and hours worked (or hours so far).
|
|
@@ -234,6 +234,18 @@ html:has(.hrl-body) { scroll-padding-top: 4rem; scroll-padding-bottom: calc(4.4r
|
|
|
234
234
|
.hrl-card--empty { text-align: center; color: var(--hrl-muted); padding: 1.8rem 1rem; }
|
|
235
235
|
.hrl-card--warn { background: var(--hrl-warn-bg); border-color: transparent; }
|
|
236
236
|
.hrl-card > p:first-child { margin-top: 0; }
|
|
237
|
+
/* Vertical rhythm: forms, buttons and disclosures stacked in a card never
|
|
238
|
+
touch (only .hrl-field carried spacing, so a button above a form, or an
|
|
239
|
+
open disclosure above the next control, sat flush). 12px >= the 8px
|
|
240
|
+
minimum gap between touch targets. */
|
|
241
|
+
.hrl-card > :is(form, details, .button_to) + *,
|
|
242
|
+
.hrl-card > * + :is(form, details, .button_to) { margin-top: .75rem; }
|
|
243
|
+
details[open] > summary.hrl-btn { margin-bottom: .75rem; }
|
|
244
|
+
.hrl-links { display: flex; flex-wrap: wrap; gap: var(--hrl-gap-target) 1rem; margin-bottom: var(--hrl-gap-target); }
|
|
245
|
+
[data-hrl-crm] select { margin-top: var(--hrl-gap-target); }
|
|
246
|
+
.hrl-checkins + .hrl-card__link { margin-top: var(--hrl-gap-target); }
|
|
247
|
+
.hrl-punch-line + .hrl-punch-line { margin-top: var(--hrl-gap-target); }
|
|
248
|
+
details > form > button[type=submit], details > form > .hrl-btn { margin-top: .25rem; }
|
|
237
249
|
.hrl-card__link, .hrl-backlink { display: inline-flex; gap: var(--hrl-s-1); align-items: center; min-height: var(--hrl-target); min-width: var(--hrl-target); font-size: var(--hrl-t-small); font-weight: 600; }
|
|
238
250
|
.hrl-listrow { display: grid; grid-template-columns: 1fr auto; align-items: start; gap: var(--hrl-s-1) var(--hrl-s-3); padding: var(--hrl-s-3) 0; border-bottom: 1px solid var(--hrl-line); min-height: var(--hrl-target); color: var(--hrl-ink); overflow-wrap: anywhere; }
|
|
239
251
|
.hrl-listrow:last-child { border-bottom: 0; }
|
|
@@ -608,7 +620,7 @@ a.hrl-tree__name { display: inline-flex; align-items: center; min-height: var(--
|
|
|
608
620
|
.hrl-chart-tip { position: absolute; z-index: 30; max-width: 16rem; padding: .4rem .6rem; border-radius: var(--hrl-radius-sm); background: var(--hrl-ink); color: var(--hrl-card); font-size: var(--hrl-t-small); pointer-events: none; }
|
|
609
621
|
[data-hrl-tip]:focus { outline: 2px solid var(--hrl-accent); outline-offset: 1px; }
|
|
610
622
|
.hrl-custom-status { display: inline-flex; gap: .3rem; align-items: center; overflow-wrap: anywhere; }
|
|
611
|
-
.hrl-emoji-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(2.75rem, 1fr)); gap:
|
|
623
|
+
.hrl-emoji-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(2.75rem, 1fr)); gap: var(--hrl-gap-target); margin-top: var(--hrl-gap-target); }
|
|
612
624
|
.hrl-emoji { min-height: var(--hrl-target); border: 1px solid var(--hrl-line); border-radius: var(--hrl-radius-sm); background: var(--hrl-card); font-size: 1.25rem; cursor: pointer; }
|
|
613
625
|
/* Today's check-ins: one initials badge per person; hover/tap shows times. */
|
|
614
626
|
.hrl-checkins + .hrl-checkins { margin-top: .6rem; }
|
|
@@ -43,7 +43,7 @@ module HrLite
|
|
|
43
43
|
raise ActiveRecord::RecordNotFound if employee.id == hr_current_user.id
|
|
44
44
|
|
|
45
45
|
result = WorkModeSwitch.call(user: employee, mode: params[:mode], source: "hr", actor: hr_current_user,
|
|
46
|
-
params: params.permit(:title, :client, :destination, :reason, :minutes, :until, :category, :crm_ref))
|
|
46
|
+
params: params.permit(:title, :client, :destination, :reason, :minutes, :until, :category, :crm_ref, :duration))
|
|
47
47
|
flash_for = result.ok? ? { notice: "#{hr_display_name(employee)} is now: #{result.event.label}." } : { alert: result.error }
|
|
48
48
|
redirect_back fallback_location: admin_attendance_path(employee.id), **flash_for
|
|
49
49
|
end
|
|
@@ -5,7 +5,7 @@ module HrLite
|
|
|
5
5
|
|
|
6
6
|
def create
|
|
7
7
|
result = WorkModeSwitch.call(user: hr_current_user, mode: params[:mode], source: "employee", actor: hr_current_user,
|
|
8
|
-
params: params.permit(:title, :client, :destination, :reason, :minutes, :until, :category, :crm_ref))
|
|
8
|
+
params: params.permit(:title, :client, :destination, :reason, :minutes, :until, :category, :crm_ref, :duration))
|
|
9
9
|
if result.ok?
|
|
10
10
|
redirect_to attendance_path, notice: "You are now: #{result.event.label}."
|
|
11
11
|
else
|
|
@@ -13,6 +13,17 @@ module HrLite
|
|
|
13
13
|
|
|
14
14
|
def self.call(...) = new(...).call
|
|
15
15
|
|
|
16
|
+
# How long a mode lasts, picked per use (owner, 2026-10-13): 15 min up to a day.
|
|
17
|
+
DURATIONS = { "15" => 15.minutes, "30" => 30.minutes, "60" => 1.hour, "120" => 2.hours,
|
|
18
|
+
"240" => 4.hours, "day" => 1.day }.freeze
|
|
19
|
+
DURATION_LABELS = { "15" => "15 min", "30" => "30 min", "60" => "1 h", "120" => "2 h", "240" => "4 h", "day" => "1 day" }.freeze
|
|
20
|
+
|
|
21
|
+
# The lengths offered for a mode: breaks never past their company limit.
|
|
22
|
+
def self.duration_choices(mode)
|
|
23
|
+
cap = { "lunch" => Setting.instance.lunch_break_minutes, "short_break" => Setting.instance.short_break_minutes }[mode.to_s]
|
|
24
|
+
cap ? DURATIONS.keys.select { |k| DURATIONS[k] <= cap.minutes } : DURATIONS.keys
|
|
25
|
+
end
|
|
26
|
+
|
|
16
27
|
CLEAR_AFTER = { "30" => 30.minutes, "60" => 1.hour, "240" => 4.hours, "today" => :today, "never" => nil }.freeze
|
|
17
28
|
|
|
18
29
|
# A person's own status, Slack-style: works with or without a check-in
|
|
@@ -120,10 +131,18 @@ module HrLite
|
|
|
120
131
|
day = @now.in_time_zone(HrLite.config.time_zone).all_day
|
|
121
132
|
return "You have already taken lunch today." if WorkModeEvent.where(user_id: @user.id, mode: "lunch", started_at: day).exists?
|
|
122
133
|
|
|
123
|
-
|
|
134
|
+
capped_break(Setting.instance.lunch_break_minutes)
|
|
124
135
|
end
|
|
125
136
|
|
|
126
|
-
def short_break_attrs =
|
|
137
|
+
def short_break_attrs = capped_break(Setting.instance.short_break_minutes)
|
|
138
|
+
|
|
139
|
+
# A break runs the picked length (never past the limit), or the full limit.
|
|
140
|
+
def capped_break(limit)
|
|
141
|
+
return base.merge(ends_at: @now + limit.minutes) unless @p[:duration]
|
|
142
|
+
return "That break length is over the #{limit}-minute limit." unless self.class.duration_choices(@mode).include?(@p[:duration])
|
|
143
|
+
|
|
144
|
+
base.merge(ends_at: @now + DURATIONS[@p[:duration]])
|
|
145
|
+
end
|
|
127
146
|
|
|
128
147
|
def parse_until
|
|
129
148
|
Time.zone.parse("#{@now.to_date} #{@p[:until]}") if @p[:until]
|
|
@@ -131,12 +150,18 @@ module HrLite
|
|
|
131
150
|
nil
|
|
132
151
|
end
|
|
133
152
|
|
|
134
|
-
#
|
|
153
|
+
# A picked length (15 min … 1 day), or minutes / an "HH:MM" end time;
|
|
154
|
+
# always in the future and at most a day away.
|
|
135
155
|
def timed(attrs, required: true)
|
|
136
|
-
|
|
156
|
+
return "Pick how long from the choices." if @p[:duration] && !DURATIONS.key?(@p[:duration])
|
|
157
|
+
|
|
158
|
+
ends_at = if @p[:duration] then @now + DURATIONS[@p[:duration]]
|
|
159
|
+
elsif @p[:minutes] then @now + @p[:minutes].to_i.minutes
|
|
160
|
+
else parse_until
|
|
161
|
+
end
|
|
137
162
|
return attrs unless ends_at || required
|
|
138
|
-
return "Pick
|
|
139
|
-
return "The end time must be later
|
|
163
|
+
return "Pick how long." unless ends_at
|
|
164
|
+
return "The end time must be later than now, and at most a day away." unless ends_at > @now && ends_at <= @now + 1.day
|
|
140
165
|
|
|
141
166
|
attrs.merge(ends_at: ends_at)
|
|
142
167
|
end
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
<% [ [ "In", record.check_in_at, record.check_in_lat, record.check_in_lng ],
|
|
40
40
|
[ "Out", record.check_out_at, record.check_out_lat, record.check_out_lng ] ].each do |label, at, lat, lng| %>
|
|
41
41
|
<% next unless at %>
|
|
42
|
-
<div><%= label %> <%= at.strftime("%H:%M") %> · <%= record.remote? ? record.work_mode_label : (HrLite::OfficeLocation.place(lat, lng, @offices) || "No GPS") %>
|
|
42
|
+
<div class="hrl-punch-line"><%= label %> <%= at.strftime("%H:%M") %> · <%= record.remote? ? record.work_mode_label : (HrLite::OfficeLocation.place(lat, lng, @offices) || "No GPS") %>
|
|
43
43
|
<% if lat && lng %>
|
|
44
44
|
· <a class="hrl-card__link" href="https://www.google.com/maps?q=<%= lat %>,<%= lng %>" target="_blank" rel="noopener noreferrer">Map<span class="hrl-sr-only"> of <%= label.downcase %> <%= record.date.strftime("%-d %b") %></span></a>
|
|
45
45
|
<% end %>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<%# How long a mode lasts: real radios styled as chips (selected state exposed).
|
|
2
|
+
locals: mode, id, optional (adds "Until I change it", the default) %>
|
|
3
|
+
<% choices = HrLite::WorkModeSwitch.duration_choices(mode)
|
|
4
|
+
optional = local_assigns[:optional]
|
|
5
|
+
default = optional ? "" : (choices.include?("30") ? "30" : choices.last) %>
|
|
6
|
+
<fieldset class="hrl-field">
|
|
7
|
+
<legend class="hrl-small"><strong>How long</strong></legend>
|
|
8
|
+
<div class="hrl-chiprow" role="radiogroup" aria-label="How long">
|
|
9
|
+
<% if optional %>
|
|
10
|
+
<label><input type="radio" name="duration" value="" <%= "checked" if default == "" %>><span>Until I change it</span></label>
|
|
11
|
+
<% end %>
|
|
12
|
+
<% choices.each do |key| %>
|
|
13
|
+
<label><input type="radio" name="duration" id="<%= id %>-<%= key %>" value="<%= key %>" <%= "checked" if key == default %>><span><%= HrLite::WorkModeSwitch::DURATION_LABELS[key] %></span></label>
|
|
14
|
+
<% end %>
|
|
15
|
+
</div>
|
|
16
|
+
</fieldset>
|
|
@@ -91,12 +91,15 @@
|
|
|
91
91
|
</div>
|
|
92
92
|
<% end %>
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
94
|
+
<%# Links wrap as whole items; no separator dots left dangling at a line end. %>
|
|
95
|
+
<div class="hrl-links">
|
|
96
|
+
<a class="hrl-card__link" href="<%= hr_lite.attendance_path %>">View my month</a>
|
|
97
|
+
<% if HrLite.config.office_geofence %>
|
|
98
|
+
<a class="hrl-card__link" href="<%= hr_lite.wfh_requests_path %>">Work from home</a>
|
|
99
|
+
<a class="hrl-card__link" href="<%= hr_lite.work_locations_path %>">WFH locations</a>
|
|
100
|
+
<a class="hrl-card__link" href="<%= hr_lite.work_time_path %>">My work time</a>
|
|
101
|
+
<% end %>
|
|
102
|
+
</div>
|
|
100
103
|
|
|
101
104
|
<% if local_assigns.key?(:attention) %>
|
|
102
105
|
<div class="hrl-row">
|
|
@@ -22,22 +22,37 @@
|
|
|
22
22
|
<details>
|
|
23
23
|
<summary class="hrl-btn hrl-btn--block">Change my mode</summary>
|
|
24
24
|
<div class="hrl-mode-switch">
|
|
25
|
-
|
|
26
|
-
<%=
|
|
27
|
-
|
|
28
|
-
<%= HrLite::WorkModeEvent.label(mode) %>
|
|
29
|
-
<% end %>
|
|
25
|
+
<%= button_to hr_lite.work_mode_path, params: { mode: "office" }, method: :post, class: "hrl-btn hrl-btn--block" do %>
|
|
26
|
+
<%= hrl_mode_icon(HrLite::WorkStatus::Entry.new(mode: "office", label: HrLite::WorkModeEvent.label("office"))) %>
|
|
27
|
+
<%= HrLite::WorkModeEvent.label("office") %>
|
|
30
28
|
<% end %>
|
|
31
29
|
<% if HrLite::WfhRequest.approved_for(hr_current_user.id, Date.current) || HrLite::EmployeeProfile.where(user_id: hr_current_user.id).pick(:permanent_wfh) %>
|
|
32
30
|
<%= button_to "Work from home", hr_lite.work_mode_path, params: { mode: "wfh" }, method: :post, class: "hrl-btn hrl-btn--block" %>
|
|
33
31
|
<% end %>
|
|
32
|
+
<%# Breaks: one tap for the full limit, or pick a shorter length when the limit allows a choice. %>
|
|
33
|
+
<% %w[lunch short_break].each do |mode| %>
|
|
34
|
+
<% if HrLite::WorkModeSwitch.duration_choices(mode).size > 1 %>
|
|
35
|
+
<details>
|
|
36
|
+
<summary class="hrl-btn hrl-btn--block"><%= HrLite::WorkModeEvent.label(mode) %></summary>
|
|
37
|
+
<%= form_with url: hr_lite.work_mode_path, method: :post, local: true do %>
|
|
38
|
+
<input type="hidden" name="mode" value="<%= mode %>">
|
|
39
|
+
<%= render "hr_lite/attendance/duration_chips", mode: mode, id: "dur-#{mode}" %>
|
|
40
|
+
<button type="submit" class="hrl-btn hrl-btn--primary">Start <%= HrLite::WorkModeEvent.label(mode).downcase %></button>
|
|
41
|
+
<% end %>
|
|
42
|
+
</details>
|
|
43
|
+
<% else %>
|
|
44
|
+
<%= button_to hr_lite.work_mode_path, params: { mode: mode }, method: :post, class: "hrl-btn hrl-btn--block" do %>
|
|
45
|
+
<%= hrl_mode_icon(HrLite::WorkStatus::Entry.new(mode: mode, label: HrLite::WorkModeEvent.label(mode))) %>
|
|
46
|
+
<%= HrLite::WorkModeEvent.label(mode) %>
|
|
47
|
+
<% end %>
|
|
48
|
+
<% end %>
|
|
49
|
+
<% end %>
|
|
34
50
|
|
|
35
51
|
<details>
|
|
36
52
|
<summary class="hrl-btn hrl-btn--block">Busy / focus</summary>
|
|
37
53
|
<%= form_with url: hr_lite.work_mode_path, method: :post, local: true do %>
|
|
38
54
|
<input type="hidden" name="mode" value="busy">
|
|
39
|
-
|
|
40
|
-
<%= select_tag :minutes, options_for_select([ [ "30 minutes", 30 ], [ "1 hour", 60 ], [ "2 hours", 120 ] ]), id: "busy-minutes" %></div>
|
|
55
|
+
<%= render "hr_lite/attendance/duration_chips", mode: "busy", id: "dur-busy" %>
|
|
41
56
|
<button type="submit" class="hrl-btn hrl-btn--primary">Set busy</button>
|
|
42
57
|
<% end %>
|
|
43
58
|
</details>
|
|
@@ -48,7 +63,7 @@
|
|
|
48
63
|
<div class="hrl-field"><label for="mt-title">Meeting</label><input class="hrl-input" id="mt-title" name="title" maxlength="120" required></div>
|
|
49
64
|
<div class="hrl-field"><label for="mt-client">Client (optional)</label><input class="hrl-input" id="mt-client" name="client" maxlength="120"></div>
|
|
50
65
|
<%= render "hr_lite/attendance/crm_picker", id: "mt-crm" %>
|
|
51
|
-
|
|
66
|
+
<%= render "hr_lite/attendance/duration_chips", mode: "meeting", id: "dur-meeting" %>
|
|
52
67
|
<button type="submit" class="hrl-btn hrl-btn--primary">Start meeting</button>
|
|
53
68
|
<% end %>
|
|
54
69
|
</details>
|
|
@@ -60,7 +75,7 @@
|
|
|
60
75
|
<div class="hrl-field"><label for="tr-why">Purpose</label><input class="hrl-input" id="tr-why" name="title" maxlength="120" required></div>
|
|
61
76
|
<div class="hrl-field"><label for="tr-client">Client (optional)</label><input class="hrl-input" id="tr-client" name="client" maxlength="120"></div>
|
|
62
77
|
<%= render "hr_lite/attendance/crm_picker", id: "tr-crm" %>
|
|
63
|
-
|
|
78
|
+
<%= render "hr_lite/attendance/duration_chips", mode: "travelling", id: "dur-travelling", optional: true %>
|
|
64
79
|
<button type="submit" class="hrl-btn hrl-btn--primary">Start travelling</button>
|
|
65
80
|
<% end %>
|
|
66
81
|
</details>
|
|
@@ -72,7 +87,7 @@
|
|
|
72
87
|
<div class="hrl-field"><label for="om-client">Client or prospect<%= " (or link a lead above)" if HrLite.config.crm_search %></label><input class="hrl-input" id="om-client" name="client" maxlength="120"<%= " required" unless HrLite.config.crm_search %>></div>
|
|
73
88
|
<div class="hrl-field"><label for="om-why">Purpose</label><input class="hrl-input" id="om-why" name="title" maxlength="120" required></div>
|
|
74
89
|
<div class="hrl-field"><label for="om-where">Where (optional)</label><input class="hrl-input" id="om-where" name="destination" maxlength="120"></div>
|
|
75
|
-
|
|
90
|
+
<%= render "hr_lite/attendance/duration_chips", mode: "outside_meeting", id: "dur-om", optional: true %>
|
|
76
91
|
<button type="submit" class="hrl-btn hrl-btn--primary">Start client meeting</button>
|
|
77
92
|
<% end %>
|
|
78
93
|
</details>
|
|
@@ -82,7 +97,7 @@
|
|
|
82
97
|
<input type="hidden" name="mode" value="out_of_office">
|
|
83
98
|
<div class="hrl-field"><label for="oo-why">Reason</label>
|
|
84
99
|
<%= select_tag :category, options_for_select(HrLite::WorkModeSwitch::OUT_OF_OFFICE_REASONS.invert), id: "oo-why" %></div>
|
|
85
|
-
|
|
100
|
+
<%= render "hr_lite/attendance/duration_chips", mode: "out_of_office", id: "dur-oo" %>
|
|
86
101
|
<p class="hrl-small hrl-muted">Away for a full day? <a href="<%= hr_lite.new_leave_request_path %>">Apply for leave</a> instead.</p>
|
|
87
102
|
<button type="submit" class="hrl-btn hrl-btn--primary">Set out of office</button>
|
|
88
103
|
<% end %>
|
data/lib/hr_lite/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: hr_lite
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.36.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- kshitiz sinha
|
|
@@ -350,6 +350,7 @@ files:
|
|
|
350
350
|
- app/views/hr_lite/attendance/_calendar_connection.html.erb
|
|
351
351
|
- app/views/hr_lite/attendance/_crm_picker.html.erb
|
|
352
352
|
- app/views/hr_lite/attendance/_custom_status.html.erb
|
|
353
|
+
- app/views/hr_lite/attendance/_duration_chips.html.erb
|
|
353
354
|
- app/views/hr_lite/attendance/_month_grid.html.erb
|
|
354
355
|
- app/views/hr_lite/attendance/_penalties.html.erb
|
|
355
356
|
- app/views/hr_lite/attendance/_punch_card.html.erb
|