hr_lite 0.54.0 → 0.55.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 +60 -0
- data/app/assets/javascripts/hr_lite/confirm.js +9 -5
- data/app/assets/javascripts/hr_lite/countdown.js +9 -2
- data/app/assets/javascripts/hr_lite/search.js +1 -1
- data/app/assets/stylesheets/hr_lite/hr_lite.css +18 -9
- data/app/controllers/hr_lite/admin/attendances_controller.rb +10 -3
- data/app/controllers/hr_lite/admin/audit_logs_controller.rb +1 -0
- data/app/controllers/hr_lite/admin/employees_controller.rb +11 -4
- data/app/controllers/hr_lite/admin/expenses_controller.rb +4 -0
- data/app/controllers/hr_lite/admin/hr_requests_controller.rb +4 -0
- data/app/controllers/hr_lite/admin/leave_requests_controller.rb +4 -1
- data/app/controllers/hr_lite/admin/payroll_runs_controller.rb +9 -5
- data/app/controllers/hr_lite/admin/reports_controller.rb +3 -2
- data/app/controllers/hr_lite/admin/salary_structures_controller.rb +2 -0
- data/app/controllers/hr_lite/admin/tax_declarations_controller.rb +13 -3
- data/app/controllers/hr_lite/approvals_controller.rb +1 -0
- data/app/controllers/hr_lite/home_controller.rb +2 -2
- data/app/controllers/hr_lite/regularization_requests_controller.rb +4 -4
- data/app/controllers/hr_lite/tax_declarations_controller.rb +9 -5
- data/app/helpers/hr_lite/application_helper.rb +14 -8
- data/app/jobs/hr_lite/attendance_close_job.rb +1 -1
- data/app/jobs/hr_lite/payroll_compute_job.rb +12 -0
- data/app/models/concerns/hr_lite/audited.rb +3 -1
- data/app/models/concerns/hr_lite/encrypted_money.rb +8 -5
- data/app/models/hr_lite/attendance_record.rb +14 -0
- data/app/models/hr_lite/comp_off_request.rb +2 -0
- data/app/models/hr_lite/document.rb +1 -1
- data/app/models/hr_lite/expense.rb +3 -0
- data/app/models/hr_lite/hr_request.rb +2 -0
- data/app/models/hr_lite/kudo.rb +2 -2
- data/app/models/hr_lite/leave_request.rb +25 -6
- data/app/models/hr_lite/payroll_line_item.rb +8 -0
- data/app/models/hr_lite/payroll_run.rb +15 -6
- data/app/models/hr_lite/regularization_request.rb +2 -2
- data/app/models/hr_lite/resignation.rb +5 -1
- data/app/models/hr_lite/role.rb +5 -0
- data/app/models/hr_lite/salary_slip.rb +1 -1
- data/app/models/hr_lite/tax_declaration.rb +5 -0
- data/app/services/hr_lite/attendance_puncher.rb +4 -0
- data/app/services/hr_lite/overview_query.rb +0 -12
- data/app/services/hr_lite/work_status.rb +5 -2
- data/app/views/hr_lite/admin/assets/index.html.erb +9 -2
- data/app/views/hr_lite/admin/attendances/show.html.erb +3 -1
- data/app/views/hr_lite/admin/attendances/summary.html.erb +5 -2
- data/app/views/hr_lite/admin/audit_logs/index.html.erb +6 -4
- data/app/views/hr_lite/admin/ca_reviews/index.html.erb +3 -3
- data/app/views/hr_lite/admin/checklists/index.html.erb +3 -3
- data/app/views/hr_lite/admin/comp_off_requests/index.html.erb +1 -2
- data/app/views/hr_lite/admin/comp_off_requests/show.html.erb +5 -5
- data/app/views/hr_lite/admin/designation_changes/new.html.erb +4 -3
- data/app/views/hr_lite/admin/documents/index.html.erb +8 -3
- data/app/views/hr_lite/admin/documents/show.html.erb +1 -0
- data/app/views/hr_lite/admin/employees/_form.html.erb +12 -5
- data/app/views/hr_lite/admin/employees/show.html.erb +38 -32
- data/app/views/hr_lite/admin/expenses/index.html.erb +3 -2
- data/app/views/hr_lite/admin/holidays/index.html.erb +5 -4
- data/app/views/hr_lite/admin/hr_requests/index.html.erb +1 -1
- data/app/views/hr_lite/admin/hr_requests/show.html.erb +4 -3
- data/app/views/hr_lite/admin/leave_balances/index.html.erb +6 -4
- data/app/views/hr_lite/admin/leave_requests/index.html.erb +1 -2
- data/app/views/hr_lite/admin/leave_requests/show.html.erb +5 -3
- data/app/views/hr_lite/admin/leave_types/edit.html.erb +4 -4
- data/app/views/hr_lite/admin/leave_types/index.html.erb +3 -3
- data/app/views/hr_lite/admin/loans/index.html.erb +1 -1
- data/app/views/hr_lite/admin/loans/show.html.erb +8 -12
- data/app/views/hr_lite/admin/office_locations/edit.html.erb +4 -4
- data/app/views/hr_lite/admin/payroll_runs/index.html.erb +2 -2
- data/app/views/hr_lite/admin/payroll_runs/show.html.erb +10 -7
- data/app/views/hr_lite/admin/policies/index.html.erb +1 -1
- data/app/views/hr_lite/admin/policies/new.html.erb +2 -2
- data/app/views/hr_lite/admin/regularization_requests/index.html.erb +3 -4
- data/app/views/hr_lite/admin/regularization_requests/show.html.erb +4 -5
- data/app/views/hr_lite/admin/reports/index.html.erb +3 -6
- data/app/views/hr_lite/admin/reports/show.html.erb +16 -2
- data/app/views/hr_lite/admin/roles/_form.html.erb +6 -5
- data/app/views/hr_lite/admin/salary_slips/show.html.erb +3 -2
- data/app/views/hr_lite/admin/salary_structures/_form.html.erb +1 -0
- data/app/views/hr_lite/admin/settings/edit.html.erb +5 -1
- data/app/views/hr_lite/admin/statutory_rate_cards/_form.html.erb +3 -3
- data/app/views/hr_lite/admin/statutory_rate_cards/index.html.erb +3 -1
- data/app/views/hr_lite/admin/tax_declarations/index.html.erb +1 -1
- data/app/views/hr_lite/admin/tax_declarations/show.html.erb +2 -0
- data/app/views/hr_lite/admin/wfh_requests/index.html.erb +17 -16
- data/app/views/hr_lite/admin/wfh_requests/show.html.erb +2 -4
- data/app/views/hr_lite/admin/work_locations/index.html.erb +1 -1
- data/app/views/hr_lite/admin/work_modes/index.html.erb +1 -1
- data/app/views/hr_lite/approvals/index.html.erb +1 -1
- data/app/views/hr_lite/attendance/_month_grid.html.erb +9 -9
- data/app/views/hr_lite/attendance/_penalties.html.erb +2 -2
- data/app/views/hr_lite/attendance/_punch_card.html.erb +2 -4
- data/app/views/hr_lite/attendance/_slot.html.erb +9 -6
- data/app/views/hr_lite/attendance/_trail.html.erb +1 -1
- data/app/views/hr_lite/attendance/_work_mode.html.erb +1 -1
- data/app/views/hr_lite/attendance/show.html.erb +4 -4
- data/app/views/hr_lite/calendar/show.html.erb +2 -2
- data/app/views/hr_lite/comp_off_requests/index.html.erb +1 -1
- data/app/views/hr_lite/comp_off_requests/new.html.erb +1 -1
- data/app/views/hr_lite/documents/_upload_form.html.erb +1 -1
- data/app/views/hr_lite/employee_profiles/show.html.erb +4 -2
- data/app/views/hr_lite/home/index.html.erb +2 -2
- data/app/views/hr_lite/id_cards/show.html.erb +5 -1
- data/app/views/hr_lite/leave_requests/_balance_chips.html.erb +2 -2
- data/app/views/hr_lite/leave_requests/new.html.erb +17 -8
- data/app/views/hr_lite/leave_requests/show.html.erb +2 -0
- data/app/views/hr_lite/org/show.html.erb +1 -1
- data/app/views/hr_lite/regularization_requests/index.html.erb +7 -7
- data/app/views/hr_lite/regularization_requests/new.html.erb +7 -5
- data/app/views/hr_lite/salary_slips/_slip_detail.html.erb +2 -2
- data/app/views/hr_lite/salary_slips/pdf.html.erb +1 -1
- data/app/views/hr_lite/tax/_computation.html.erb +1 -1
- data/app/views/hr_lite/tax_declarations/show.html.erb +4 -4
- data/app/views/hr_lite/team/show.html.erb +8 -7
- data/app/views/hr_lite/timelines/show.html.erb +1 -0
- data/app/views/hr_lite/wfh_requests/index.html.erb +3 -3
- data/app/views/hr_lite/work_time/_bars.html.erb +1 -1
- data/app/views/hr_lite/work_time/_donut.html.erb +1 -1
- data/app/views/hr_lite/work_time/_report.html.erb +14 -8
- data/app/views/layouts/hr_lite/application.html.erb +1 -0
- data/lib/hr_lite/notifications.rb +5 -2
- data/lib/hr_lite/version.rb +1 -1
- metadata +2 -1
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
<div class="hrl-row hrl-right">
|
|
15
15
|
<% { present: "ok", half_day: "ok", leave: nil, absent: "bad" }.each do |kind, tone| %>
|
|
16
16
|
<% code, word = HrLite::ApplicationHelper::DAY_STATES[kind] %>
|
|
17
|
-
<span class="<%= class_names("hrl-badge", "hrl-badge--#{tone}": tone) %>" role="img" aria-label="<%= word %> <%= pluralize(@counts[kind], "day") %>"><%=
|
|
17
|
+
<span class="<%= class_names("hrl-badge", "hrl-badge--#{tone}": tone) %>" role="img" aria-label="<%= word %> <%= pluralize(@counts[kind], "day") %>"><%= word %> <%= @counts[kind] %></span>
|
|
18
18
|
<% end %>
|
|
19
19
|
</div>
|
|
20
20
|
</div>
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
link_builder: ->(d) { hr_lite.attendance_path(month: @month.strftime("%Y-%m"), day: d.iso8601, anchor: "day") if d <= Date.current } %>
|
|
23
23
|
<p class="hrl-small hrl-muted hrl-mt">Forgot to check in or out on some day?</p>
|
|
24
24
|
<div class="hrl-row">
|
|
25
|
-
<a class="hrl-btn" href="<%= hr_lite.new_regularization_request_path %>">
|
|
26
|
-
<a class="hrl-btn" href="<%= hr_lite.regularization_requests_path %>">
|
|
25
|
+
<a class="hrl-btn" href="<%= hr_lite.new_regularization_request_path %>">Fix a day</a>
|
|
26
|
+
<a class="hrl-btn" href="<%= hr_lite.regularization_requests_path %>">Day fixes</a>
|
|
27
27
|
</div>
|
|
28
28
|
</section>
|
|
29
29
|
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
<% if leave %><dt>Leave</dt><dd><%= leave.leave_type.name %></dd><% end %>
|
|
49
49
|
<% if @day_info.kind == :holiday %><dt>Holiday</dt><dd><%= HrLite::Holiday.company_wide.for_office(HrLite::WorkingCalendar.office_for(hr_current_user.id)).where(date: @day).pick(:name) %></dd><% end %>
|
|
50
50
|
<% if rec&.flagged? %><dt>Flag</dt><dd><%= rec.flag_note %></dd><% end %>
|
|
51
|
-
<% if @day_ticket %><dt>
|
|
51
|
+
<% if @day_ticket %><dt>Day fix</dt><dd><%= hrl_request_status_badge(@day_ticket.status) %> <%= @day_ticket.times_label %></dd><% end %>
|
|
52
52
|
</dl>
|
|
53
53
|
<% unless %i[holiday weekend leave].include?(@day_info.kind) || @day_ticket&.pending? %>
|
|
54
54
|
<a class="hrl-btn hrl-btn--primary hrl-btn--block hrl-mt" href="<%= hr_lite.new_regularization_request_path(date: @day) %>">Fix this day</a>
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
<% (first.cwday - 1).times do %><div></div><% end %>
|
|
21
21
|
<% (first..@month.end_of_month).each do |date| %>
|
|
22
22
|
<% cell_class, code =
|
|
23
|
-
if @
|
|
23
|
+
if @calendar.holiday?(date) then [ "hrl-mgrid__day--holiday", "H" ]
|
|
24
24
|
elsif @calendar.weekend?(date) then "hrl-mgrid__day--weekend"
|
|
25
25
|
elsif @leaves[date].any? then [ "hrl-mgrid__day--leave", "#{@leaves[date].size} off" ]
|
|
26
26
|
end %>
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
|
|
41
41
|
<%# Names only — the leave type is the employee's own business. Leave keeps its label's "(first half)" note. %>
|
|
42
42
|
<% day = ->(d) { d.strftime("%a %-d %b") }
|
|
43
|
-
agenda = @holidays.values.flatten.map { |h| [ h.date, "#{day.(h.date)}: #{h.name} (holiday)" ] } +
|
|
43
|
+
agenda = @holidays.values.flatten.map { |h| [ h.date, "#{day.(h.date)}: #{h.name} (#{h.optional ? 'optional holiday' : 'holiday'})" ] } +
|
|
44
44
|
@leaves.values.flatten.uniq.map { |r| [ [ r.start_date, first ].max, "#{[ r.start_date, r.end_date ].uniq.map(&day).join(' – ')}#{r.date_range_label[/ \(.+\)/]}: #{hr_display_name(r.user)}, on leave" ] } %>
|
|
45
45
|
<% if agenda.any? %>
|
|
46
46
|
<ul class="hrl-feed hrl-small hrl-mt">
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
<section class="hrl-card">
|
|
11
11
|
<p class="hrl-small hrl-muted">
|
|
12
|
-
Worked on a weekend or holiday? Request a comp-off — once
|
|
12
|
+
Worked on a weekend or holiday? Request a comp-off — once it is approved,
|
|
13
13
|
<%= @comp_off_type ? "your #{@comp_off_type.name} balance is credited" : "a day is credited to your balance" %>
|
|
14
14
|
and you spend it through the normal leave flow.
|
|
15
15
|
</p>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<%= f.label :half_day, "I worked only half the day (credits 0.5)" %>
|
|
19
19
|
</div>
|
|
20
20
|
<div class="hrl-field">
|
|
21
|
-
<%= f.label :reason, "What did you work on?
|
|
21
|
+
<%= f.label :reason, "What did you work on?" %>
|
|
22
22
|
<%= f.text_area :reason, required: true %>
|
|
23
23
|
</div>
|
|
24
24
|
<div class="hrl-form-actions">
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<%# locals: document, url (where it posts), open (start expanded) — one upload form for My documents and HR's view of a person. %>
|
|
2
2
|
<details class="hrl-card" <%= "open" if open || document.errors.any? %>>
|
|
3
|
-
<summary class="hrl-btn
|
|
3
|
+
<summary class="hrl-btn">Upload a document</summary>
|
|
4
4
|
<%= form_with model: document, url: url, method: :post, local: true, class: "hrl-mt" do |f| %>
|
|
5
5
|
<%= render "hr_lite/shared/form_errors", record: document %>
|
|
6
6
|
<div class="hrl-field">
|
|
@@ -19,8 +19,10 @@
|
|
|
19
19
|
<dt>Bank</dt><dd><%= [ @profile.bank_name, @profile.masked_account ].compact.join(" ").presence || "—" %></dd>
|
|
20
20
|
</dl>
|
|
21
21
|
<a class="hrl-btn hrl-mt" href="<%= hr_lite.org_path %>">Org chart</a>
|
|
22
|
-
<p class="hrl-small hrl-muted hrl-mt">Something wrong?
|
|
23
|
-
|
|
22
|
+
<p class="hrl-small hrl-muted hrl-mt">Something wrong?
|
|
23
|
+
<%= link_to_if hr_can?("hr_request.raise"), "Ask leadership to correct it", hr_lite.new_hr_request_path, class: "hrl-card__link" %> — changes are audited.</p>
|
|
24
|
+
<hr>
|
|
25
|
+
<a class="hrl-btn" href="<%= hr_lite.resignation_path %>">Resignation</a>
|
|
24
26
|
</section>
|
|
25
27
|
<% else %>
|
|
26
28
|
<p class="hrl-card hrl-card--empty">Your HR profile has not been set up yet.</p>
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
<% @mine_pending.each do |req| %>
|
|
21
21
|
<% title, detail, href = case req
|
|
22
22
|
when HrLite::LeaveRequest then [ req.leave_type.name, "#{req.date_range_label} · #{pluralize(hrl_days(req.days_count), 'day')}", hr_lite.leave_request_path(req) ]
|
|
23
|
-
when HrLite::RegularizationRequest then [ "
|
|
23
|
+
when HrLite::RegularizationRequest then [ "Day fix", "#{req.date.strftime('%a %-d %b')} · #{req.times_label}", hr_lite.regularization_requests_path ]
|
|
24
|
+
when HrLite::WfhRequest then [ "Work from home", [ req.start_date, req.end_date ].uniq.map { |d| d.strftime("%a %-d %b") }.join(" – "), hr_lite.wfh_requests_path ]
|
|
24
25
|
else [ "Comp-off", "Worked #{req.date_worked.strftime('%a %-d %b')}", hr_lite.comp_off_requests_path ]
|
|
25
26
|
end %>
|
|
26
27
|
<a class="hrl-listrow" href="<%= href %>">
|
|
@@ -37,7 +38,6 @@
|
|
|
37
38
|
<section class="hrl-card">
|
|
38
39
|
<h2 class="hrl-card__title">On leave this week</h2>
|
|
39
40
|
<%= render "hr_lite/leave_requests/out_list", leaves: @out_week, empty: "Nobody is on leave this week." %>
|
|
40
|
-
<a class="hrl-card__link" href="<%= hr_lite.team_path %>">Team board</a>
|
|
41
41
|
</section>
|
|
42
42
|
|
|
43
43
|
<% if @awards.any? %>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<% content_for(:page_title) { "ID card" } %>
|
|
2
2
|
<% content_for(:tabrow) { render "hr_lite/shared/person_tabs", user: @user } if params[:user_id] %>
|
|
3
|
+
<% content_for(:back, "Employees") if params[:user_id] %>
|
|
3
4
|
<% company = HrLite.config.company.call %>
|
|
4
5
|
<% brand = company[:brand].presence || company[:name].presence || "HR" %>
|
|
5
6
|
<% own = @user == hr_current_user %>
|
|
@@ -24,7 +25,7 @@
|
|
|
24
25
|
</div>
|
|
25
26
|
<div class="hrl-idcard__photo">
|
|
26
27
|
<% if @profile.photo.attached? %>
|
|
27
|
-
<img src="<%= Rails.application.routes.url_helpers.rails_storage_proxy_path(@profile.photo, disposition: "inline", only_path: true) %>" alt="<%= hr_display_name(@user) %>">
|
|
28
|
+
<img src="<%= Rails.application.routes.url_helpers.rails_storage_proxy_path(@profile.photo, disposition: "inline", only_path: true, expires_in: 1.hour) %>" alt="<%= hr_display_name(@user) %>">
|
|
28
29
|
<% else %>
|
|
29
30
|
<span><%= hr_display_name(@user).to_s.scan(/\b[[:alpha:]]/).first(2).join.upcase %></span>
|
|
30
31
|
<% end %>
|
|
@@ -63,6 +64,9 @@
|
|
|
63
64
|
</div>
|
|
64
65
|
|
|
65
66
|
<div class="hrl-idcard-actions">
|
|
67
|
+
<% if own && (@profile.blood_group.blank? || @profile.emergency_contact.blank?) && hr_can?("hr_request.raise") %>
|
|
68
|
+
<a class="hrl-card__link hrl-idcard-help" href="<%= hr_lite.new_hr_request_path %>">Missing details? Ask HR</a>
|
|
69
|
+
<% end %>
|
|
66
70
|
<button type="button" class="hrl-btn hrl-btn--primary" data-hrl-print>Print</button>
|
|
67
71
|
<button type="button" class="hrl-btn" data-hrl-share="id-card-<%= @profile.employee_code %>.png">Share</button>
|
|
68
72
|
<% if own && @print_requested %>
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
<% if types.any? %>
|
|
7
7
|
<div class="hrl-row">
|
|
8
8
|
<% types.each do |type| %>
|
|
9
|
-
<% balance = HrLite::LeaveBalance.for(user, type, HrLite::LeaveYear.current_key) %>
|
|
9
|
+
<% balance = HrLite::LeaveBalance.for(user, type, HrLite::LeaveYear.current_key); entitled = balance.entitled %>
|
|
10
10
|
<% pending = HrLite::LeaveRequest.pending.where(user_id: user.id, leave_type_id: type.id, start_date: HrLite::LeaveYear.range(HrLite::LeaveYear.current_key)).sum(&:days_count) %>
|
|
11
11
|
<span class="hrl-badge hrl-badge--type" style="--hrl-type: <%= type.color %>">
|
|
12
|
-
<%= type.name %> · <%= hrl_days(balance.
|
|
12
|
+
<%= type.name %> · <%= hrl_days(entitled - balance.used) %> of <%= hrl_days(entitled) %> left<% if pending.positive? %> · <%= hrl_days(pending) %> pending<% end %><% if (held = balance.penalty_days).positive? %> · <%= hrl_days(held) %> held for penalties<% end %>
|
|
13
13
|
</span>
|
|
14
14
|
<% end %>
|
|
15
15
|
</div>
|
|
@@ -7,38 +7,47 @@
|
|
|
7
7
|
<section class="hrl-card">
|
|
8
8
|
<%= form_with model: @request, url: hr_lite.leave_requests_path, local: true do |f| %>
|
|
9
9
|
<%= render "hr_lite/shared/form_errors", record: @request %>
|
|
10
|
+
<% bad = ->(attr) { "hrl-#{attr}-error" if @request.errors[attr].any? } %>
|
|
11
|
+
<% say = ->(attr) { tag.p(@request.errors.full_messages_for(attr).to_sentence, id: bad.(attr), class: "hrl-hint hrl-hint--bad") if bad.(attr) } %>
|
|
10
12
|
<div class="hrl-field">
|
|
11
13
|
<%= f.label :leave_type_id, "Type", id: "hrl-leave-type", for: nil %>
|
|
12
14
|
<% left = @balances.index_by(&:leave_type_id) %>
|
|
13
|
-
|
|
14
|
-
<% HrLite::LeaveType.active.each_with_index do |type, i| %>
|
|
15
|
+
<%= tag.div(class: "hrl-chiprow", role: "radiogroup", aria: { labelledby: "hrl-leave-type", describedby: bad.(:leave_type) }) do %>
|
|
16
|
+
<% HrLite::LeaveType.active.reject { |t| t.comp_off && (b = left[t.id]) && b.available <= 0 }.each_with_index do |type, i| %>
|
|
15
17
|
<label><%= f.radio_button :leave_type_id, type.id, required: i.zero? %><span><%= type.name.sub(/ leave\z/i, "") %><%= " · #{hrl_days(left[type.id].available)} left" if left[type.id] %><% if left[type.id] && (held = left[type.id].penalty_days).positive? %> · <%= hrl_days(held) %> held for penalties<% end %><%= " · Unpaid" unless type.paid %></span></label>
|
|
16
18
|
<% end %>
|
|
17
|
-
|
|
19
|
+
<% end %>
|
|
20
|
+
<%= say.(:leave_type) %>
|
|
18
21
|
<p class="hrl-hint">Days beyond your balance are unpaid (loss of pay), cut from that month's salary.<% if (capped = @balances.find { |b| b.penalty_days.positive? }) %> Today you can apply for at most <%= hrl_days(capped.applicable) %> days of <%= capped.leave_type.name %>, <%= hrl_days(capped.penalty_days) %> held for penalties.<% end %></p>
|
|
22
|
+
<% if (profile = HrLite::EmployeeProfile.find_by(user_id: hr_current_user.id))&.on_probation? %>
|
|
23
|
+
<p class="hrl-hint">On probation until <%= profile.probation_until.strftime("%d %b %Y") %>: at most 1 day of leave a month, of any type.</p>
|
|
24
|
+
<% end %>
|
|
19
25
|
</div>
|
|
20
26
|
<div class="hrl-field">
|
|
21
27
|
<%= f.label :start_date, "From" %>
|
|
22
|
-
<%= f.date_field :start_date, required: true %>
|
|
28
|
+
<%= f.date_field :start_date, required: true, aria: { describedby: bad.(:start_date) } %>
|
|
29
|
+
<%= say.(:start_date) %>
|
|
23
30
|
</div>
|
|
24
31
|
<div class="hrl-field">
|
|
25
32
|
<%= f.label :end_date, "To (leave blank for one day)" %>
|
|
26
|
-
<%= f.date_field :end_date %>
|
|
33
|
+
<%= f.date_field :end_date, aria: { describedby: bad.(:end_date) } %>
|
|
34
|
+
<%= say.(:end_date) %>
|
|
27
35
|
</div>
|
|
28
36
|
<div class="hrl-field">
|
|
29
37
|
<%= f.label :half_day_part, "Day", id: "hrl-day-part", for: nil %>
|
|
30
|
-
|
|
38
|
+
<%= tag.div(class: "hrl-chiprow", role: "radiogroup", aria: { labelledby: "hrl-day-part", describedby: bad.(:half_day) }) do %>
|
|
31
39
|
<% HrLite::DAY_PARTS.each do |label, value| %>
|
|
32
40
|
<label><%= f.radio_button :half_day_part, value %><span><%= label %></span></label>
|
|
33
41
|
<% end %>
|
|
34
|
-
|
|
42
|
+
<% end %>
|
|
43
|
+
<%= say.(:half_day) %>
|
|
35
44
|
</div>
|
|
36
45
|
<div class="hrl-field">
|
|
37
46
|
<%= f.label :reason, "Reason (optional)" %>
|
|
38
47
|
<%= f.text_area :reason %>
|
|
39
48
|
</div>
|
|
40
49
|
<div class="hrl-form-actions">
|
|
41
|
-
<%= f.submit "
|
|
50
|
+
<%= f.submit "Send for approval", class: "hrl-btn hrl-btn--primary" %>
|
|
42
51
|
<a class="hrl-btn" href="<%= hr_lite.leave_requests_path %>">Cancel</a>
|
|
43
52
|
</div>
|
|
44
53
|
<% end %>
|
|
@@ -19,5 +19,7 @@
|
|
|
19
19
|
<%= button_to "Cancel this leave", hr_lite.cancel_leave_request_path(@request), method: :post,
|
|
20
20
|
class: "hrl-btn hrl-btn--danger hrl-mt",
|
|
21
21
|
form: { data: { turbo_confirm: "Cancel this leave request?" } } %>
|
|
22
|
+
<% elsif @request.approved? %>
|
|
23
|
+
<p class="hrl-hint hrl-mt">This leave has started, so it can no longer be cancelled here. Ask HR if it needs cancelling.</p>
|
|
22
24
|
<% end %>
|
|
23
25
|
</section>
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
<% content_for(:page_title) { "
|
|
1
|
+
<% content_for(:page_title) { "Day fixes" } %>
|
|
2
2
|
<% content_for(:back, "Attendance") %>
|
|
3
3
|
<div class="hrl-page__head">
|
|
4
|
-
<h1 class="hrl-page__title">
|
|
4
|
+
<h1 class="hrl-page__title">Day fixes</h1>
|
|
5
5
|
<div class="hrl-page__actions">
|
|
6
|
-
<a class="hrl-btn hrl-btn--primary" href="<%= hr_lite.new_regularization_request_path %>">
|
|
6
|
+
<a class="hrl-btn hrl-btn--primary" href="<%= hr_lite.new_regularization_request_path %>">Fix a day</a>
|
|
7
7
|
</div>
|
|
8
8
|
</div>
|
|
9
9
|
|
|
10
10
|
<section class="hrl-card">
|
|
11
11
|
<p class="hrl-small hrl-muted">
|
|
12
|
-
Forgot to check in or out?
|
|
13
|
-
|
|
12
|
+
Forgot to check in or out? Fix a day with the actual times — once your
|
|
13
|
+
manager or HR approves, your attendance for that day is fixed automatically.
|
|
14
14
|
</p>
|
|
15
15
|
<% if @requests.any? %>
|
|
16
16
|
<div class="hrl-table-wrap">
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
<td data-label="" class="<%= 'hrl-cell--void' unless request.pending? %>">
|
|
27
27
|
<% if request.pending? %>
|
|
28
28
|
<%= button_to "Cancel", hr_lite.cancel_regularization_request_path(request), method: :post,
|
|
29
|
-
class: "hrl-btn hrl-btn--danger", form: { data: { turbo_confirm: "Cancel this
|
|
29
|
+
class: "hrl-btn hrl-btn--danger", form: { data: { turbo_confirm: "Cancel this day fix?" } } %>
|
|
30
30
|
<% end %>
|
|
31
31
|
</td>
|
|
32
32
|
</tr>
|
|
@@ -36,6 +36,6 @@
|
|
|
36
36
|
</div>
|
|
37
37
|
<%= hrl_pagination %>
|
|
38
38
|
<% else %>
|
|
39
|
-
<p class="hrl-card--empty">No
|
|
39
|
+
<p class="hrl-card--empty">No day fixes yet.</p>
|
|
40
40
|
<% end %>
|
|
41
41
|
</section>
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
<% content_for(:page_title) { "
|
|
1
|
+
<% content_for(:page_title) { "Fix a day" } %>
|
|
2
2
|
<% content_for(:back, "Attendance") %>
|
|
3
3
|
<div class="hrl-page__head">
|
|
4
|
-
<h1 class="hrl-page__title">
|
|
4
|
+
<h1 class="hrl-page__title">Fix a day</h1>
|
|
5
5
|
</div>
|
|
6
6
|
|
|
7
7
|
<section class="hrl-card">
|
|
8
8
|
<%= form_with model: @request, url: hr_lite.regularization_requests_path, scope: :regularization_request, local: true do |f| %>
|
|
9
9
|
<%= render "hr_lite/shared/form_errors", record: @request %>
|
|
10
10
|
<% if (rule = HrLite.config.self_regularization) %>
|
|
11
|
-
<p class="hrl-small hrl-muted">A missed punch or short day on a working day from today back to <%= rule[:within_days] %> days ago is fixed as soon as you submit, up to <%= rule[:per_week] %> times a week. Anything else goes to HR
|
|
11
|
+
<p class="hrl-small hrl-muted">A missed punch or short day on a working day from today back to <%= rule[:within_days] %> days ago is fixed as soon as you submit, up to <%= rule[:per_week] %> times a week. Anything else goes to your manager or HR for approval.</p>
|
|
12
12
|
<% end %>
|
|
13
13
|
<div class="hrl-field">
|
|
14
14
|
<%= f.label :date, "Which day needs fixing?" %>
|
|
@@ -25,11 +25,13 @@
|
|
|
25
25
|
<p class="hrl-hint">Leave blank if only check-in is missing. Short day? Leave both times blank and give the reason.</p>
|
|
26
26
|
</div>
|
|
27
27
|
<div class="hrl-field">
|
|
28
|
-
<%= f.label :reason, "Why does this day need fixing?
|
|
28
|
+
<%= f.label :reason, "Why does this day need fixing?" %>
|
|
29
29
|
<%= f.text_area :reason, required: true %>
|
|
30
30
|
</div>
|
|
31
|
+
<% fix_now = rule && @request.date && (probe = HrLite::RegularizationRequest.new(date: @request.date, user_id: hr_current_user.id)).self_fixable? %>
|
|
32
|
+
<% if fix_now %><p class="hrl-hint"><%= @request.date.strftime("%a %-d %b") %> is fixed as soon as you submit. Self-fixes left this week: <%= probe.self_fixes_left %>.</p><% end %>
|
|
31
33
|
<div class="hrl-form-actions">
|
|
32
|
-
<%= f.submit "
|
|
34
|
+
<%= f.submit fix_now ? "Fix now" : "Send for approval", class: "hrl-btn hrl-btn--primary" %>
|
|
33
35
|
<a class="hrl-btn" href="<%= hr_lite.regularization_requests_path %>">Cancel</a>
|
|
34
36
|
</div>
|
|
35
37
|
<% end %>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<div class="hrl-kpi"><div class="hrl-kpi__value"><%= hrl_money(slip.net_pay) %></div><div class="hrl-kpi__label">Net pay</div></div>
|
|
4
4
|
<div class="hrl-kpi"><div class="hrl-kpi__value"><%= hrl_money(slip.gross_earnings) %></div><div class="hrl-kpi__label">Gross</div></div>
|
|
5
5
|
<div class="hrl-kpi"><div class="hrl-kpi__value"><%= hrl_money(slip.total_deductions) %></div><div class="hrl-kpi__label">Deductions</div></div>
|
|
6
|
-
<div class="hrl-kpi"><div class="hrl-kpi__value"><%= hrl_days(slip.payable_days) %> / <%= hrl_days(slip.effective_lop_days) %></div><div class="hrl-kpi__label">Payable / LOP days</div></div>
|
|
6
|
+
<div class="hrl-kpi"><div class="hrl-kpi__value"><%= hrl_days(slip.payable_days) %> / <%= hrl_days(slip.effective_lop_days) %></div><div class="hrl-kpi__label">Payable / <abbr title="Loss of pay: unpaid days">LOP</abbr> days</div></div>
|
|
7
7
|
</div>
|
|
8
8
|
<div class="hrl-grid hrl-grid--2">
|
|
9
9
|
<section class="hrl-card">
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
<%# Out-of-window days are named rather than silently dropped: without them
|
|
47
47
|
a mid-month joiner's row does not add up to the month. %>
|
|
48
48
|
<dt>Days</dt>
|
|
49
|
-
<dd><%= hrl_days(slip.payable_days) %> payable / <%= hrl_days(slip.effective_lop_days) %> LOP
|
|
49
|
+
<dd><%= hrl_days(slip.payable_days) %> payable / <%= hrl_days(slip.effective_lop_days) %> <abbr title="Loss of pay: unpaid days">LOP</abbr><% if slip.out_of_window_days.to_f.positive? %>
|
|
50
50
|
/ <%= hrl_days(slip.out_of_window_days) %> outside employment<% end %> of <%= slip.days_in_month %></dd>
|
|
51
51
|
<% if profile %>
|
|
52
52
|
<dt>Employee code</dt><dd><%= profile.employee_code %></dd>
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
</td>
|
|
34
34
|
</tr>
|
|
35
35
|
<tr>
|
|
36
|
-
<td>Payable days: <%= @slip.payable_days %> / <%= @slip.days_in_month %> (
|
|
36
|
+
<td>Payable days: <%= @slip.payable_days.to_s.delete_suffix(".0") %> / <%= @slip.days_in_month %> (unpaid <%= @slip.effective_lop_days.to_s.delete_suffix(".0") %>)</td>
|
|
37
37
|
<td class="num"><% if @profile&.masked_account %>Bank: <%= @profile.bank_name %> <%= @profile.masked_account %><% end %></td>
|
|
38
38
|
</tr>
|
|
39
39
|
</tbody>
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
<table class="hrl-table hrl-tax-working__table">
|
|
35
35
|
<tbody>
|
|
36
36
|
<tr><td>Salary paid so far this year</td><td class="hrl-num"><%= hrl_rupees(tax.paid_gross) %></td></tr>
|
|
37
|
-
<tr><td>Expected for the rest of the year (<%= hrl_rupees(tax.structure.monthly_gross) %> × <%= s.months_left %>)</td><td class="hrl-num"><%= hrl_rupees(tax.projected_gross) %></td></tr>
|
|
37
|
+
<tr><td>Expected for the rest of the year, before unpaid days (<%= hrl_rupees(tax.structure.monthly_gross) %> × <%= s.months_left %>)</td><td class="hrl-num"><%= hrl_rupees(tax.projected_gross) %></td></tr>
|
|
38
38
|
<tr class="hrl-tax-working__total"><td>Gross salary for the year</td><td class="hrl-num"><%= hrl_rupees(s.gross) %></td></tr>
|
|
39
39
|
<% if s.hra_exemption.positive? %><tr><td>Less HRA exemption (from rent)</td><td class="hrl-num">− <%= hrl_rupees(s.hra_exemption) %></td></tr><% end %>
|
|
40
40
|
<tr><td>Less standard deduction</td><td class="hrl-num">− <%= hrl_rupees(s.standard_deduction) %></td></tr>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<% content_for(:page_title) { "Tax" } %>
|
|
2
2
|
<% submittable = @declaration.persisted? && (@declaration.draft? || @declaration.rejected?) %>
|
|
3
|
-
<% locked = @declaration.verified? %>
|
|
3
|
+
<% locked = @declaration.verified? || @declaration.submitted? %>
|
|
4
4
|
<div class="hrl-page__head">
|
|
5
5
|
<h1 class="hrl-page__title">Tax — <%= @declaration.label %></h1>
|
|
6
6
|
</div>
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
<section class="hrl-card">
|
|
15
15
|
<p>
|
|
16
|
-
Declaration:
|
|
16
|
+
Declaration: <% if @declaration.verified? %><span class="hrl-badge hrl-badge--ok">Verified</span><% elsif @declaration.rejected? %><span class="hrl-badge hrl-badge--bad">Sent back</span><% else %><%= hrl_request_status_badge(@declaration.status) %><% end %>
|
|
17
17
|
<% if @declaration.note.present? %><span class="hrl-small hrl-muted">— <%= @declaration.note %></span><% end %>
|
|
18
18
|
</p>
|
|
19
19
|
<fieldset class="hrl-field" <%= "disabled" if locked %>>
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
</div>
|
|
27
27
|
<% end %>
|
|
28
28
|
</fieldset>
|
|
29
|
-
</
|
|
29
|
+
</section>
|
|
30
30
|
|
|
31
31
|
<details class="hrl-card" <%= "open" if @declaration.regime != "new" || @declaration.errors.any? %>>
|
|
32
32
|
<summary class="hrl-btn">Investments and rent (old regime only)</summary>
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
<% if submittable %><button type="submit" name="submit_to_hr" value="1" class="hrl-btn hrl-btn--primary">Submit to HR</button><% end %>
|
|
70
70
|
</div>
|
|
71
71
|
<% else %>
|
|
72
|
-
<p class="hrl-small hrl-muted"
|
|
72
|
+
<p class="hrl-small hrl-muted"><%= @declaration.verified? ? "HR has verified this year's declaration." : "Sent to HR to check the proof." %> To change anything, ask HR through Help › Ask HR.</p>
|
|
73
73
|
<% end %>
|
|
74
74
|
<% end %>
|
|
75
75
|
<% content_for(:scripts) { javascript_include_tag "hr_lite/tax_preview", defer: true } %>
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
<% content_for(:page_title) { "Team" } %>
|
|
1
|
+
<% content_for(:page_title) { "Team board" } %>
|
|
2
2
|
<div class="hrl-page__head">
|
|
3
|
-
<h1 class="hrl-page__title">Team — <%= @date == Date.current ? "today" : @date.strftime("%A, %d %B") %></h1>
|
|
3
|
+
<h1 class="hrl-page__title">Team board — <%= @date == Date.current ? "today" : @date.strftime("%A, %d %B") %></h1>
|
|
4
4
|
<div class="hrl-page__actions">
|
|
5
|
-
<a class="hrl-btn"
|
|
5
|
+
<a class="hrl-btn" href="<%= hr_lite.team_path(date: (@date - 1).strftime("%Y-%m-%d")) %>"><span aria-hidden="true">‹</span> <%= @date == Date.current ? "Yesterday" : "Previous day" %></a>
|
|
6
6
|
<% unless @date == Date.current %>
|
|
7
7
|
<a class="hrl-btn" href="<%= hr_lite.team_path %>">Today</a>
|
|
8
8
|
<% end %>
|
|
9
9
|
<% if @date < Date.current %>
|
|
10
|
-
<a class="hrl-btn"
|
|
10
|
+
<a class="hrl-btn" href="<%= hr_lite.team_path(date: (@date + 1).strftime("%Y-%m-%d")) %>">Next day <span aria-hidden="true">›</span></a>
|
|
11
11
|
<% end %>
|
|
12
12
|
</div>
|
|
13
13
|
</div>
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
<div class="hrl-kpi"><div class="hrl-kpi__value"><%= kpis[:checked_in] %></div><div class="hrl-kpi__label">Checked in</div></div>
|
|
18
18
|
<div class="hrl-kpi"><div class="hrl-kpi__value"><%= kpis[:on_leave] %></div><div class="hrl-kpi__label">On leave</div></div>
|
|
19
19
|
<div class="hrl-kpi"><div class="hrl-kpi__value"><%= kpis[:not_in] %></div><div class="hrl-kpi__label">Not in</div></div>
|
|
20
|
-
<div class="hrl-kpi"><div class="hrl-kpi__value"><%= @board.rows.size %></div><div class="hrl-kpi__label">
|
|
20
|
+
<div class="hrl-kpi"><div class="hrl-kpi__value"><%= @board.rows.size %></div><div class="hrl-kpi__label">People</div></div>
|
|
21
21
|
</div>
|
|
22
22
|
|
|
23
23
|
<% if @statuses %>
|
|
@@ -48,7 +48,8 @@
|
|
|
48
48
|
<span class="hrl-badge hrl-badge--muted">+<%= row.profile.buffer_minutes / 60 %>h buffer · <%= HrLite::EmployeeProfile::BUFFER_REASONS[row.profile.buffer_reason] %></span><% end %></span>
|
|
49
49
|
</td>
|
|
50
50
|
<% if (st = @statuses&.dig(row.user.id)) %>
|
|
51
|
-
|
|
51
|
+
<%# A derived status (offline, leave, holiday) repeats the Status cell; phone cards skip it. %>
|
|
52
|
+
<td data-label="Now" class="<%= 'hrl-cell--void' unless st.primary.event || st.custom %>"><span class="hrl-modes"><%= hrl_status_icons(st) %></span>
|
|
52
53
|
<span class="hrl-small"><%= st.primary.place || st.primary.label %><% if st.overlay %> · <%= st.overlay.label %><% end %>
|
|
53
54
|
<% if (till = st.overlay&.until || (st.primary.until if st.primary.until.respond_to?(:hour))) %> · until <%= till.strftime("%H:%M") %><% end %></span>
|
|
54
55
|
<% if st.updated_at %><span class="hrl-small hrl-muted"> · since <%= st.updated_at.strftime("%H:%M") %></span><% end %>
|
|
@@ -66,5 +67,5 @@
|
|
|
66
67
|
<p class="hrl-card--empty">Nobody to show<%= @rows ? " for these filters" : " for this date" %>.</p>
|
|
67
68
|
<% end %>
|
|
68
69
|
<p class="hrl-small hrl-muted hrl-mt">Forgot a punch yourself?
|
|
69
|
-
<a class="hrl-card__link" href="<%= hr_lite.new_regularization_request_path %>">
|
|
70
|
+
<a class="hrl-card__link" href="<%= hr_lite.new_regularization_request_path %>">Fix a day</a></p>
|
|
70
71
|
</section>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<% content_for(:page_title) { "Timeline — #{hr_display_name(@person)}" } %>
|
|
2
2
|
<% content_for(:tabrow) { render "hr_lite/shared/person_tabs", user: @person } if params[:user_id] %>
|
|
3
|
+
<% content_for(:back, "Org chart") if params[:user_id] %>
|
|
3
4
|
<% year_path = ->(y) { @person.id == hr_current_user.id ? hr_lite.timeline_path(year: y) : hr_lite.person_timeline_path(@person.id, year: y) } %>
|
|
4
5
|
<% name = hr_display_name(@person) %>
|
|
5
6
|
<div class="hrl-page__head">
|
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
|
|
7
7
|
<section class="hrl-card">
|
|
8
8
|
<h2 class="hrl-card__title">Request WFH</h2>
|
|
9
|
-
<p class="hrl-small hrl-muted">Pick dates from today up to <%= HrLite::WfhRequest::MAX_DAYS_AHEAD %> days ahead. HR approves or rejects. While a request is waiting you can still check in away from the office;
|
|
9
|
+
<p class="hrl-small hrl-muted">Pick dates from today up to <%= HrLite::WfhRequest::MAX_DAYS_AHEAD %> days ahead. Your manager or HR approves or rejects. While a request is waiting you can still check in away from the office; they see the reason. A rejected date cannot.</p>
|
|
10
10
|
<%= form_with model: @request, url: hr_lite.wfh_requests_path, scope: :wfh_request, local: true do |f| %>
|
|
11
11
|
<%= render "hr_lite/shared/form_errors", record: @request %>
|
|
12
12
|
<% min, max = @window.first.iso8601, @window.last.iso8601 %>
|
|
13
13
|
<div class="hrl-field"><%= f.label :start_date, "From" %><%= f.date_field :start_date, min: min, max: max, required: true %></div>
|
|
14
14
|
<div class="hrl-field"><%= f.label :end_date, "To (same day for one day)" %><%= f.date_field :end_date, min: min, max: max, required: true %></div>
|
|
15
15
|
<div class="hrl-field"><%= f.label :reason, "Why do you need WFH?" %><%= f.text_area :reason, rows: 3, required: true %></div>
|
|
16
|
-
<div class="hrl-form-actions"><%= f.submit "Send
|
|
16
|
+
<div class="hrl-form-actions"><%= f.submit "Send for approval", class: "hrl-btn hrl-btn--primary" %></div>
|
|
17
17
|
<% end %>
|
|
18
18
|
</section>
|
|
19
19
|
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
<% code, word, mod = marks[@day_status[date]] %>
|
|
28
28
|
<div class="<%= class_names("hrl-mgrid__day", "hrl-mgrid__day--#{mod}": mod, "hrl-mgrid__day--today": date == Date.current) %>"
|
|
29
29
|
role="group" aria-label="<%= date.strftime('%-d %b') %><%= ", #{word}" if word %>">
|
|
30
|
-
<span class="hrl-mgrid__num"><%= date.day %></span>
|
|
30
|
+
<span class="hrl-mgrid__num"><%= date.day == 1 ? date.strftime("%-d %b") : date.day %></span>
|
|
31
31
|
<% if code %><span class="hrl-mgrid__tag"><%= code %></span><% end %>
|
|
32
32
|
</div>
|
|
33
33
|
<% end %>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<% buckets = result.by_bucket
|
|
3
3
|
totals = buckets.transform_values { |m| m.except("offline").values.sum }
|
|
4
4
|
max = [ totals.values.max.to_f, 1 ].max
|
|
5
|
-
w = 12; gap =
|
|
5
|
+
w = 12; gap = 8; h = 100 # gap 8 units: bars ~11px apart on screen
|
|
6
6
|
fmt = { day: "%-d", week: "%-d %b", month: "%b" }[period.bucket] %>
|
|
7
7
|
<section class="hrl-card">
|
|
8
8
|
<h2 class="hrl-card__title">By <%= period.bucket %></h2>
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
</tbody>
|
|
30
30
|
</table>
|
|
31
31
|
</div>
|
|
32
|
-
<p class="hrl-small hrl-muted">Shares are of tracked time (<%= hrl_duration(total) %>: working, breaks and out of office). Offline (<%= hrl_duration(result.offline) %>) is not included.</p>
|
|
32
|
+
<p class="hrl-small hrl-muted">Shares are of tracked time (<%= hrl_duration(total) %>: working, breaks and out of office). Offline (<%= hrl_duration(result.offline) %>) is not included. Stretches: how many separate times each mode ran.</p>
|
|
33
33
|
<% else %>
|
|
34
34
|
<p class="hrl-card--empty">Only offline time recorded.</p>
|
|
35
35
|
<% end %>
|
|
@@ -20,12 +20,16 @@
|
|
|
20
20
|
<a class="hrl-btn" href="<%= path.(period: period.kind, date: period.shift(1)) %>" aria-label="Next <%= period.kind %>">›</a>
|
|
21
21
|
<% end %>
|
|
22
22
|
</div>
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
<%# One picker on show: the date range stays folded unless it is what was asked for. %>
|
|
24
|
+
<%= tag.details open: params[:period] == "custom" do %>
|
|
25
|
+
<summary class="hrl-card__link">Pick your own dates</summary>
|
|
26
|
+
<%= form_with url: path.({}), method: :get, local: true, class: "hrl-row" do %>
|
|
27
|
+
<input type="hidden" name="period" value="custom">
|
|
28
|
+
<% (local_assigns[:keep] || {}).each do |k, v| %><input type="hidden" name="<%= k %>" value="<%= v %>"><% end %>
|
|
29
|
+
<div class="hrl-field"><label for="wt-from">From</label><%= date_field_tag :from, period.from, max: Date.current, id: "wt-from" %></div>
|
|
30
|
+
<div class="hrl-field"><label for="wt-to">To</label><%= date_field_tag :to, period.to, max: Date.current, id: "wt-to" %></div>
|
|
31
|
+
<div class="hrl-form-actions"><button type="submit" class="hrl-btn">Show range</button></div>
|
|
32
|
+
<% end %>
|
|
29
33
|
<% end %>
|
|
30
34
|
</section>
|
|
31
35
|
|
|
@@ -35,7 +39,8 @@
|
|
|
35
39
|
<% office = result.by_mode["office"]; wfh = result.by_mode["wfh"]; field = result.by_mode["travelling"] + result.by_mode["outside_meeting"] %>
|
|
36
40
|
<div class="hrl-kpis">
|
|
37
41
|
<% [ [ "Attendance (check-in to check-out)", result.attendance ], [ "Working time (breaks included)", result.working + result.breaks ], [ "Of which breaks", result.breaks ],
|
|
38
|
-
[ "In office", office ], [ "Work from home", wfh ], [ "Travel and client meetings", field ],
|
|
42
|
+
[ "In office", office ], [ "Work from home", wfh ], [ "Travel and client meetings", field ],
|
|
43
|
+
[ "Meetings and busy", result.by_mode["meeting"] + result.by_mode["busy"] ], [ "Out of office", result.other ] ].each do |kpi_label, secs| %>
|
|
39
44
|
<div class="hrl-kpi"><div class="hrl-kpi__value"><%= hrl_duration(secs) %></div><div class="hrl-kpi__label"><%= kpi_label %></div></div>
|
|
40
45
|
<% end %>
|
|
41
46
|
</div>
|
|
@@ -62,11 +67,12 @@
|
|
|
62
67
|
<% if records.any? %>
|
|
63
68
|
<section class="hrl-card">
|
|
64
69
|
<h2 class="hrl-card__title">Check-ins</h2>
|
|
70
|
+
<% all_punches = HrLite::AttendancePunch.where(user_id: records.map(&:user_id).uniq, date: records.map(&:date)).order(:at, :id).group_by { |p| [ p.user_id, p.date ] } # one query, not one a day %>
|
|
65
71
|
<% records.each do |r| %>
|
|
66
72
|
<div class="hrl-feed__item hrl-small"><div class="hrl-feed__body">
|
|
67
73
|
<% if names %><strong><%= names[r.user_id] %></strong> · <% end %>
|
|
68
74
|
<%= r.date.strftime("%a %-d %b") %><% if r.work_mode_label %> · <%= r.work_mode_label %><% end %>
|
|
69
|
-
<%= render "hr_lite/attendance/trail", record: r %>
|
|
75
|
+
<%= render "hr_lite/attendance/trail", record: r, punches: all_punches.fetch([ r.user_id, r.date ], []) %>
|
|
70
76
|
</div></div>
|
|
71
77
|
<% end %>
|
|
72
78
|
</section>
|
|
@@ -117,6 +117,7 @@
|
|
|
117
117
|
<%= javascript_include_tag "hr_lite/select_filter", defer: true %>
|
|
118
118
|
<%= javascript_include_tag "hr_lite/search", defer: true %>
|
|
119
119
|
<dialog class="hrl-search" data-hrl-search aria-label="Search">
|
|
120
|
+
<form method="dialog" class="hrl-search__close"><button class="hrl-btn" type="submit">Close</button></form>
|
|
120
121
|
<form action="<%= hr_lite.search_path %>" method="get" role="search">
|
|
121
122
|
<label class="hrl-sr-only" for="hrl-search-q">Search screens and people</label>
|
|
122
123
|
<input id="hrl-search-q" class="hrl-input" type="search" name="q" placeholder="Search screens and people" autocomplete="off">
|
|
@@ -131,14 +131,17 @@ module HrLite
|
|
|
131
131
|
# Rescued PER RECIPIENT. Wrapping the whole loop meant one bad address
|
|
132
132
|
# or a single enqueue failure silently dropped everyone after it — on
|
|
133
133
|
# a team-wide leave notice, most of the team.
|
|
134
|
-
Array(email_to).compact.uniq
|
|
134
|
+
users = Array(email_to).compact.uniq
|
|
135
|
+
# One query for everyone's Busy/Meeting end, not one per recipient.
|
|
136
|
+
held_until = HrLite.config.office_geofence ? WorkStatus.do_not_disturb_for(users.map(&:id)) : {}
|
|
137
|
+
users.each do |user|
|
|
135
138
|
next if user.email.blank?
|
|
136
139
|
|
|
137
140
|
begin
|
|
138
141
|
mail = EventMailer.event(to: user.email, subject: title, heading: title,
|
|
139
142
|
body: body, lines: lines, path: path, link_url: link_url)
|
|
140
143
|
# Busy or in a meeting: the email waits until that ends.
|
|
141
|
-
held =
|
|
144
|
+
held = held_until[user.id]
|
|
142
145
|
held ? mail.deliver_later(wait_until: held) : mail.deliver_later
|
|
143
146
|
rescue => e
|
|
144
147
|
Rails.logger.error("[hr_lite] event email failed: #{e.class}: #{e.message}")
|
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.55.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- kshitiz sinha
|
|
@@ -171,6 +171,7 @@ files:
|
|
|
171
171
|
- app/jobs/hr_lite/leave_year_rollover_job.rb
|
|
172
172
|
- app/jobs/hr_lite/payroll_auto_approve_job.rb
|
|
173
173
|
- app/jobs/hr_lite/payroll_auto_draft_job.rb
|
|
174
|
+
- app/jobs/hr_lite/payroll_compute_job.rb
|
|
174
175
|
- app/jobs/hr_lite/slips_ready_job.rb
|
|
175
176
|
- app/jobs/hr_lite/work_mode_expire_job.rb
|
|
176
177
|
- app/mailers/hr_lite/application_mailer.rb
|