hr_lite 0.54.0 → 0.56.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.
Files changed (127) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +71 -0
  3. data/app/assets/javascripts/hr_lite/confirm.js +9 -5
  4. data/app/assets/javascripts/hr_lite/countdown.js +9 -2
  5. data/app/assets/javascripts/hr_lite/search.js +1 -1
  6. data/app/assets/stylesheets/hr_lite/hr_lite.css +18 -9
  7. data/app/controllers/hr_lite/admin/attendances_controller.rb +10 -3
  8. data/app/controllers/hr_lite/admin/audit_logs_controller.rb +1 -0
  9. data/app/controllers/hr_lite/admin/employees_controller.rb +11 -4
  10. data/app/controllers/hr_lite/admin/expenses_controller.rb +6 -2
  11. data/app/controllers/hr_lite/admin/hr_requests_controller.rb +4 -0
  12. data/app/controllers/hr_lite/admin/leave_requests_controller.rb +4 -1
  13. data/app/controllers/hr_lite/admin/payroll_runs_controller.rb +9 -5
  14. data/app/controllers/hr_lite/admin/reports_controller.rb +3 -2
  15. data/app/controllers/hr_lite/admin/salary_structures_controller.rb +2 -0
  16. data/app/controllers/hr_lite/admin/tax_declarations_controller.rb +13 -3
  17. data/app/controllers/hr_lite/approvals_controller.rb +1 -0
  18. data/app/controllers/hr_lite/home_controller.rb +2 -2
  19. data/app/controllers/hr_lite/regularization_requests_controller.rb +4 -4
  20. data/app/controllers/hr_lite/tax_declarations_controller.rb +9 -5
  21. data/app/helpers/hr_lite/application_helper.rb +20 -14
  22. data/app/jobs/hr_lite/attendance_close_job.rb +1 -1
  23. data/app/jobs/hr_lite/payroll_compute_job.rb +12 -0
  24. data/app/models/concerns/hr_lite/audited.rb +3 -1
  25. data/app/models/concerns/hr_lite/encrypted_money.rb +8 -5
  26. data/app/models/hr_lite/attendance_record.rb +14 -0
  27. data/app/models/hr_lite/benefit_enrolment.rb +3 -1
  28. data/app/models/hr_lite/comp_off_request.rb +2 -0
  29. data/app/models/hr_lite/document.rb +1 -1
  30. data/app/models/hr_lite/expense.rb +13 -5
  31. data/app/models/hr_lite/hr_request.rb +2 -0
  32. data/app/models/hr_lite/kudo.rb +2 -2
  33. data/app/models/hr_lite/leave_request.rb +25 -6
  34. data/app/models/hr_lite/payroll_line_item.rb +8 -0
  35. data/app/models/hr_lite/payroll_run.rb +18 -6
  36. data/app/models/hr_lite/regularization_request.rb +2 -2
  37. data/app/models/hr_lite/resignation.rb +5 -1
  38. data/app/models/hr_lite/role.rb +5 -0
  39. data/app/models/hr_lite/salary_slip.rb +1 -1
  40. data/app/models/hr_lite/tax_declaration.rb +5 -0
  41. data/app/services/hr_lite/attendance_puncher.rb +4 -0
  42. data/app/services/hr_lite/overview_query.rb +0 -12
  43. data/app/services/hr_lite/payroll_run_processor.rb +3 -0
  44. data/app/services/hr_lite/slip_builder.rb +17 -9
  45. data/app/services/hr_lite/work_status.rb +5 -2
  46. data/app/views/hr_lite/admin/assets/index.html.erb +9 -2
  47. data/app/views/hr_lite/admin/attendances/show.html.erb +3 -1
  48. data/app/views/hr_lite/admin/attendances/summary.html.erb +5 -2
  49. data/app/views/hr_lite/admin/audit_logs/index.html.erb +6 -4
  50. data/app/views/hr_lite/admin/ca_reviews/index.html.erb +3 -3
  51. data/app/views/hr_lite/admin/checklists/index.html.erb +3 -3
  52. data/app/views/hr_lite/admin/comp_off_requests/index.html.erb +1 -2
  53. data/app/views/hr_lite/admin/comp_off_requests/show.html.erb +5 -5
  54. data/app/views/hr_lite/admin/designation_changes/new.html.erb +4 -3
  55. data/app/views/hr_lite/admin/documents/index.html.erb +8 -3
  56. data/app/views/hr_lite/admin/documents/show.html.erb +2 -1
  57. data/app/views/hr_lite/admin/employees/_form.html.erb +12 -5
  58. data/app/views/hr_lite/admin/employees/show.html.erb +38 -32
  59. data/app/views/hr_lite/admin/expenses/index.html.erb +4 -3
  60. data/app/views/hr_lite/admin/holidays/index.html.erb +5 -4
  61. data/app/views/hr_lite/admin/hr_requests/index.html.erb +1 -1
  62. data/app/views/hr_lite/admin/hr_requests/show.html.erb +4 -3
  63. data/app/views/hr_lite/admin/leave_balances/index.html.erb +6 -4
  64. data/app/views/hr_lite/admin/leave_requests/index.html.erb +1 -2
  65. data/app/views/hr_lite/admin/leave_requests/show.html.erb +5 -3
  66. data/app/views/hr_lite/admin/leave_types/edit.html.erb +4 -4
  67. data/app/views/hr_lite/admin/leave_types/index.html.erb +3 -3
  68. data/app/views/hr_lite/admin/loans/index.html.erb +2 -2
  69. data/app/views/hr_lite/admin/loans/show.html.erb +8 -12
  70. data/app/views/hr_lite/admin/office_locations/edit.html.erb +4 -4
  71. data/app/views/hr_lite/admin/payroll_runs/index.html.erb +2 -2
  72. data/app/views/hr_lite/admin/payroll_runs/show.html.erb +10 -7
  73. data/app/views/hr_lite/admin/policies/index.html.erb +1 -1
  74. data/app/views/hr_lite/admin/policies/new.html.erb +2 -2
  75. data/app/views/hr_lite/admin/regularization_requests/index.html.erb +3 -4
  76. data/app/views/hr_lite/admin/regularization_requests/show.html.erb +4 -5
  77. data/app/views/hr_lite/admin/reports/index.html.erb +3 -6
  78. data/app/views/hr_lite/admin/reports/show.html.erb +16 -2
  79. data/app/views/hr_lite/admin/roles/_form.html.erb +6 -5
  80. data/app/views/hr_lite/admin/salary_slips/show.html.erb +3 -2
  81. data/app/views/hr_lite/admin/salary_structures/_form.html.erb +1 -0
  82. data/app/views/hr_lite/admin/settings/edit.html.erb +5 -1
  83. data/app/views/hr_lite/admin/statutory_rate_cards/_form.html.erb +3 -3
  84. data/app/views/hr_lite/admin/statutory_rate_cards/index.html.erb +3 -1
  85. data/app/views/hr_lite/admin/tax_declarations/index.html.erb +1 -1
  86. data/app/views/hr_lite/admin/tax_declarations/show.html.erb +2 -0
  87. data/app/views/hr_lite/admin/wfh_requests/index.html.erb +17 -16
  88. data/app/views/hr_lite/admin/wfh_requests/show.html.erb +2 -4
  89. data/app/views/hr_lite/admin/work_locations/index.html.erb +1 -1
  90. data/app/views/hr_lite/admin/work_modes/index.html.erb +1 -1
  91. data/app/views/hr_lite/approvals/index.html.erb +1 -1
  92. data/app/views/hr_lite/attendance/_month_grid.html.erb +9 -9
  93. data/app/views/hr_lite/attendance/_penalties.html.erb +2 -2
  94. data/app/views/hr_lite/attendance/_punch_card.html.erb +2 -4
  95. data/app/views/hr_lite/attendance/_slot.html.erb +9 -6
  96. data/app/views/hr_lite/attendance/_trail.html.erb +1 -1
  97. data/app/views/hr_lite/attendance/_work_mode.html.erb +1 -1
  98. data/app/views/hr_lite/attendance/show.html.erb +4 -4
  99. data/app/views/hr_lite/calendar/show.html.erb +2 -2
  100. data/app/views/hr_lite/comp_off_requests/index.html.erb +1 -1
  101. data/app/views/hr_lite/comp_off_requests/new.html.erb +1 -1
  102. data/app/views/hr_lite/documents/_upload_form.html.erb +1 -1
  103. data/app/views/hr_lite/employee_profiles/show.html.erb +4 -2
  104. data/app/views/hr_lite/home/index.html.erb +2 -2
  105. data/app/views/hr_lite/id_cards/show.html.erb +5 -1
  106. data/app/views/hr_lite/leave_requests/_balance_chips.html.erb +2 -2
  107. data/app/views/hr_lite/leave_requests/new.html.erb +17 -8
  108. data/app/views/hr_lite/leave_requests/show.html.erb +2 -0
  109. data/app/views/hr_lite/org/show.html.erb +1 -1
  110. data/app/views/hr_lite/regularization_requests/index.html.erb +7 -7
  111. data/app/views/hr_lite/regularization_requests/new.html.erb +7 -5
  112. data/app/views/hr_lite/salary_slips/_slip_detail.html.erb +2 -2
  113. data/app/views/hr_lite/salary_slips/pdf.html.erb +1 -1
  114. data/app/views/hr_lite/tax/_computation.html.erb +1 -1
  115. data/app/views/hr_lite/tax_declarations/show.html.erb +4 -4
  116. data/app/views/hr_lite/team/show.html.erb +8 -7
  117. data/app/views/hr_lite/timelines/show.html.erb +1 -0
  118. data/app/views/hr_lite/wfh_requests/index.html.erb +3 -3
  119. data/app/views/hr_lite/work_time/_bars.html.erb +1 -1
  120. data/app/views/hr_lite/work_time/_donut.html.erb +1 -1
  121. data/app/views/hr_lite/work_time/_report.html.erb +14 -8
  122. data/app/views/layouts/hr_lite/application.html.erb +1 -0
  123. data/db/migrate/20261017161441_make_hr_lite_benefit_enrolments_unique_while_open.rb +8 -0
  124. data/db/migrate/20261017161443_add_attendance_record_id_index_to_hr_lite_work_mode_events.rb +5 -0
  125. data/lib/hr_lite/notifications.rb +5 -2
  126. data/lib/hr_lite/version.rb +1 -1
  127. metadata +4 -1
@@ -14,8 +14,8 @@
14
14
  <tr>
15
15
  <td data-label="Employee"><%= hr_display_name(employee) %><% if @no_joining_date.include?(employee.id) %><br><span class="hrl-badge hrl-badge--bad">No joining date — no leave accrues</span><% end %></td>
16
16
  <% @types.each do |type| %>
17
- <% balance = HrLite::LeaveBalance.for(employee, type, @year) %>
18
- <td data-label="<%= type.code %>" class="hrl-num"><span><strong><%= hrl_days(balance.available) %></strong> left<br><span class="hrl-small hrl-muted"><%= hrl_days(balance.used) %>/<%= hrl_days(balance.entitled) %> used</span><% if (held = balance.penalty_days).positive? %><br><span class="hrl-small"><%= hrl_days(held) %> held for penalties</span><% end %></span></td>
17
+ <% balance = HrLite::LeaveBalance.for(employee, type, @year); used = balance.used; entitled = balance.entitled # each once: both query %>
18
+ <td data-label="<%= type.code %>" class="hrl-num"><span><strong><%= hrl_days(entitled - used) %></strong> left<br><span class="hrl-small hrl-muted"><%= hrl_days(used) %>/<%= hrl_days(entitled) %> used</span><% if (held = balance.penalty_days).positive? %><br><span class="hrl-small"><%= hrl_days(held) %> held for penalties</span><% end %></span></td>
19
19
  <% end %>
20
20
  </tr>
21
21
  <% end %>
@@ -28,10 +28,12 @@
28
28
  <section class="hrl-card">
29
29
  <h2 class="hrl-card__title">Adjust a balance</h2>
30
30
  <p class="hrl-small hrl-muted">Positive credits (comp-off), negative debits. Fully audited.</p>
31
- <%= form_with url: hr_lite.adjust_admin_leave_balances_path, method: :post, local: true do %>
31
+ <%= form_with url: hr_lite.adjust_admin_leave_balances_path, method: :post, local: true,
32
+ data: { turbo_confirm: "Change this leave balance? It shows on the person's leave at once." } do %>
32
33
  <div class="hrl-field">
33
34
  <label for="adj_user">Employee</label>
34
- <select name="user_id" id="adj_user" class="hrl-input">
35
+ <select name="user_id" id="adj_user" class="hrl-input" required data-hrl-filter="Type a name to filter">
36
+ <option value="">Choose…</option>
35
37
  <% @employees.each do |employee| %>
36
38
  <option value="<%= employee.id %>"><%= hr_display_name(employee) %></option>
37
39
  <% end %>
@@ -8,8 +8,7 @@
8
8
 
9
9
  <div class="hrl-row hrl-row--mb">
10
10
  <% HrLite::LeaveRequest::STATUSES.each do |status| %>
11
- <a class="hrl-btn <%= 'hrl-btn--primary' if @status == status %>"
12
- href="<%= hr_lite.admin_leave_requests_path(status: status) %>"><%= status.humanize %></a>
11
+ <%= link_to status.humanize, hr_lite.admin_leave_requests_path(status: status), class: "hrl-btn", aria: { current: ("true" if @status == status) } %>
13
12
  <% end %>
14
13
  </div>
15
14
 
@@ -15,7 +15,7 @@
15
15
  <% if @request.reason.present? %><dt>Reason</dt><dd><%= @request.reason %></dd><% end %>
16
16
  <% unless @request.leave_type.unlimited? %>
17
17
  <dt>Balance</dt>
18
- <dd><%= hrl_days(@balance.available) %> of <%= hrl_days(@balance.entitled) %> available</dd>
18
+ <dd><%= hrl_days(@balance.available(as_of: @request.start_date)) %> of <%= hrl_days(@balance.entitled(as_of: @request.start_date)) %> available</dd>
19
19
  <% if (@request.pending? || @request.approved?) && (short = @request.days_beyond_balance).positive? %>
20
20
  <dt>Loss of pay</dt><dd><%= hrl_days(short) %> day(s) beyond the balance will be unpaid</dd>
21
21
  <% end %>
@@ -60,7 +60,8 @@
60
60
  <%# Approve and reject are two separate forms on purpose. Rails binds the
61
61
  CSRF token to the form's OWN action, so one form retargeted with
62
62
  `formaction:` can never pass verification on the second endpoint. %>
63
- <%= form_with url: hr_lite.approve_admin_leave_request_path(@request), method: :post, local: true do %>
63
+ <%= form_with url: hr_lite.approve_admin_leave_request_path(@request), method: :post, local: true,
64
+ html: { data: { turbo_confirm: "Approve #{hr_display_name(@request.user)}'s leave, #{@request.date_range_label}?" } } do %>
64
65
  <div class="hrl-field">
65
66
  <label for="approve_note">Note (optional)</label>
66
67
  <textarea name="decision_note" id="approve_note" class="hrl-input"></textarea>
@@ -73,7 +74,8 @@
73
74
 
74
75
  <section class="hrl-card">
75
76
  <h2 class="hrl-card__title">Reject</h2>
76
- <%= form_with url: hr_lite.reject_admin_leave_request_path(@request), method: :post, local: true do %>
77
+ <%= form_with url: hr_lite.reject_admin_leave_request_path(@request), method: :post, local: true,
78
+ html: { data: { turbo_confirm: "Reject #{hr_display_name(@request.user)}'s leave, #{@request.date_range_label}?" } } do %>
77
79
  <div class="hrl-field">
78
80
  <label for="reject_note">Reason (required)</label>
79
81
  <textarea name="decision_note" id="reject_note" class="hrl-input" required></textarea>
@@ -1,10 +1,10 @@
1
1
  <% content_for(:page_title) { "Edit leave type" } %>
2
2
  <div class="hrl-page__head">
3
3
  <h1 class="hrl-page__title">Edit <%= @leave_type.name %></h1>
4
- <div class="hrl-page__actions">
5
- <%= button_to "Delete", hr_lite.admin_leave_type_path(@leave_type), method: :delete,
4
+ </div>
5
+ <%= render "form", leave_type: @leave_type, url: hr_lite.admin_leave_type_path(@leave_type), method: :patch %>
6
+ <div class="hrl-row hrl-mt">
7
+ <%= button_to "Delete", hr_lite.admin_leave_type_path(@leave_type), method: :delete,
6
8
  class: "hrl-btn hrl-btn--danger",
7
9
  form: { data: { turbo_confirm: "Delete this leave type?" } } %>
8
- </div>
9
10
  </div>
10
- <%= render "form", leave_type: @leave_type, url: hr_lite.admin_leave_type_path(@leave_type), method: :patch %>
@@ -14,13 +14,13 @@
14
14
  <% @leave_types.each do |type| %>
15
15
  <tr>
16
16
  <td data-label="Type">
17
- <span class="hrl-badge" style="background: <%= type.color %>1f; color: <%= type.color %>;"><%= type.code %></span>
17
+ <span class="hrl-badge hrl-badge--type" style="--hrl-type: <%= type.color %>"><%= type.code %></span>
18
18
  <%= type.name %>
19
19
  <% unless type.active %><span class="hrl-badge hrl-badge--muted">Inactive</span><% end %>
20
20
  </td>
21
- <td data-label="Quota" class="hrl-num"><%= type.unlimited? ? "Unlimited" : type.annual_quota.to_f %></td>
21
+ <td data-label="Quota" class="hrl-num"><%= type.unlimited? ? "Unlimited" : "#{hrl_days(type.annual_quota)} days" %></td>
22
22
  <td data-label="Accrual"><%= type.accrual.humanize %></td>
23
- <td data-label="Carry cap" class="hrl-num"><%= type.carry_forward_cap.to_f %></td>
23
+ <td data-label="Carry cap" class="hrl-num"><%= type.carry_forward_cap ? "#{hrl_days(type.carry_forward_cap)} days" : "—" %></td>
24
24
  <td data-label="Paid"><%= type.paid ? "Yes" : "No" %></td>
25
25
  <td data-label="">
26
26
  <a class="hrl-btn" href="<%= hr_lite.edit_admin_leave_type_path(type) %>">Edit</a>
@@ -10,7 +10,7 @@
10
10
  <p>Outstanding across every active loan: <strong><%= hrl_money(@outstanding) %></strong></p>
11
11
  <div class="hrl-row">
12
12
  <% HrLite::Loan::STATUSES.each do |status| %>
13
- <a class="hrl-btn <%= "hrl-btn--primary" if @status == status %>"
13
+ <a class="hrl-btn" <%= "aria-current=true" if @status == status %>
14
14
  href="<%= hr_lite.admin_loans_path(status: status) %>"><%= status.humanize %></a>
15
15
  <% end %>
16
16
  </div>
@@ -31,7 +31,7 @@
31
31
  <td data-label="Instalment" class="hrl-num"><%= hrl_money(loan.monthly_instalment) %></td>
32
32
  <td data-label="Outstanding" class="hrl-num"><%= hrl_money(loan.outstanding) %></td>
33
33
  <td data-label="">
34
- <a class="hrl-small" href="<%= hr_lite.admin_loan_path(loan) %>">Open</a>
34
+ <a class="hrl-btn" href="<%= hr_lite.admin_loan_path(loan) %>">Open</a>
35
35
  </td>
36
36
  </tr>
37
37
  <% end %>
@@ -4,18 +4,14 @@
4
4
  </div>
5
5
 
6
6
  <section class="hrl-card">
7
- <div class="hrl-table-wrap">
8
- <table class="hrl-table hrl-table--stack">
9
- <tbody>
10
- <tr><td data-label="Status"><%= @loan.status.humanize %></td></tr>
11
- <tr><td data-label="Monthly instalment"><%= hrl_money(@loan.monthly_instalment) %></td></tr>
12
- <tr><td data-label="Starts"><%= @loan.starts_on.strftime("%b %Y") %></td></tr>
13
- <tr><td data-label="Repaid"><%= hrl_money(@loan.repaid) %></td></tr>
14
- <tr><td data-label="Outstanding"><strong><%= hrl_money(@loan.outstanding) %></strong></td></tr>
15
- <% if @loan.reason.present? %><tr><td data-label="Reason"><%= @loan.reason %></td></tr><% end %>
16
- </tbody>
17
- </table>
18
- </div>
7
+ <dl class="hrl-deflist">
8
+ <dt>Status</dt><dd><%= @loan.status.humanize %></dd>
9
+ <dt>Monthly instalment</dt><dd><%= hrl_money(@loan.monthly_instalment) %></dd>
10
+ <dt>Starts</dt><dd><%= @loan.starts_on.strftime("%b %Y") %></dd>
11
+ <dt>Repaid</dt><dd><%= hrl_money(@loan.repaid) %></dd>
12
+ <dt>Outstanding</dt><dd><strong><%= hrl_money(@loan.outstanding) %></strong></dd>
13
+ <% if @loan.reason.present? %><dt>Reason</dt><dd><%= @loan.reason %></dd><% end %>
14
+ </dl>
19
15
 
20
16
  <% if @loan.active? %>
21
17
  <div class="hrl-row">
@@ -1,10 +1,10 @@
1
1
  <% content_for(:page_title) { "Edit office" } %>
2
2
  <div class="hrl-page__head">
3
3
  <h1 class="hrl-page__title">Edit <%= @office_location.name %></h1>
4
- <div class="hrl-page__actions">
5
- <%= button_to "Delete", hr_lite.admin_office_location_path(@office_location), method: :delete,
4
+ </div>
5
+ <%= render "form", office_location: @office_location, url: hr_lite.admin_office_location_path(@office_location), method: :patch %>
6
+ <div class="hrl-row hrl-mt">
7
+ <%= button_to "Delete", hr_lite.admin_office_location_path(@office_location), method: :delete,
6
8
  class: "hrl-btn hrl-btn--danger",
7
9
  form: { data: { turbo_confirm: "Remove this office?" } } %>
8
- </div>
9
10
  </div>
10
- <%= render "form", office_location: @office_location, url: hr_lite.admin_office_location_path(@office_location), method: :patch %>
@@ -10,7 +10,7 @@
10
10
  <% if @runs.any? %>
11
11
  <div class="hrl-table-wrap">
12
12
  <table class="hrl-table hrl-table--stack">
13
- <thead><tr><th>Period</th><th>Status</th><th>Slips</th><th>Net total</th><th></th></tr></thead>
13
+ <thead><tr><th>Period</th><th>Status</th><th>Slips</th><th>Net payout</th><th></th></tr></thead>
14
14
  <tbody>
15
15
  <% @runs.each do |run| %>
16
16
  <tr>
@@ -19,7 +19,7 @@
19
19
  <span class="hrl-badge <%= { "published" => "hrl-badge--ok", "finalized" => "hrl-badge--warn", "draft" => "hrl-badge--muted" }[run.status] %>"><%= run.status.humanize %></span>
20
20
  </td>
21
21
  <td data-label="Slips" class="hrl-num"><%= run.salary_slips.size %></td>
22
- <td data-label="Net total" class="hrl-num"><%= hrl_money(run.total_net) %></td>
22
+ <td data-label="Net payout" class="hrl-num"><%= hrl_money(run.total_net) %></td>
23
23
  <td data-label=""><a class="hrl-btn" href="<%= hr_lite.admin_payroll_run_path(run) %>">Open</a></td>
24
24
  </tr>
25
25
  <% end %>
@@ -2,7 +2,7 @@
2
2
  <div class="hrl-page__head">
3
3
  <h1 class="hrl-page__title">Payroll — <%= @run.label %></h1>
4
4
  <div class="hrl-page__actions">
5
- <a class="hrl-btn" href="<%= hr_lite.admin_payroll_line_items_path(month: @run.period_month.strftime("%Y-%m")) %>">One-off items</a>
5
+ <% if hr_can?("payroll.manage", scope: :all) %><a class="hrl-btn" href="<%= hr_lite.admin_payroll_line_items_path(month: @run.period_month.strftime("%Y-%m")) %>">One-off items</a><% end %>
6
6
  <a class="hrl-btn" href="<%= hr_lite.admin_payroll_runs_path %>">All runs</a>
7
7
  </div>
8
8
  </div>
@@ -29,7 +29,7 @@
29
29
  <% manager = hr_can?("payroll.manage", scope: :all) %>
30
30
  <% if manager && (@run.draft? || @run.review? || @run.processing?) %>
31
31
  <%= button_to @run.draft? ? "Compute" : "Recompute", hr_lite.compute_admin_payroll_run_path(@run),
32
- method: :post, class: "hrl-btn",
32
+ method: :post, class: @run.draft? ? "hrl-btn hrl-btn--primary" : "hrl-btn",
33
33
  form: { data: { turbo_confirm: ("Recompute #{@run.label}? This clears #{hr_display_name(@run.first_approved_by)}'s approval." if @run.first_approved_by) } } %>
34
34
  <% end %>
35
35
  <% if @run.review? && hr_can?("payroll.approve", scope: :all) && @run.first_approved_by_id != hr_current_user.id %>
@@ -56,6 +56,9 @@
56
56
  <p class="hrl-small hrl-muted hrl-mt">
57
57
  <% if @run.review? %>
58
58
  <%= @run.first_approved_by ? "Approved by #{hr_display_name(@run.first_approved_by)} on #{@run.first_approved_at.strftime('%-d %b')} — one more approver (HR or Super Admin) publishes it." : "Needs two approvals (HR and Super Admin). If nobody approves by the 3rd, a run with no blocking warnings is approved automatically." %>
59
+ <% elsif @run.processing? %>
60
+ <% content_for(:head) { tag.meta("http-equiv": "refresh", content: "3") } %>
61
+ Computing… this page refreshes until the slips are ready.
59
62
  <% elsif @run.auto_approved_at %>
60
63
  Approved automatically on <%= @run.auto_approved_at.strftime("%-d %b") %> — nobody had approved it by the 3rd.
61
64
  <% end %>
@@ -66,20 +69,20 @@
66
69
  <% if @slips.any? %>
67
70
  <div class="hrl-table-wrap">
68
71
  <table class="hrl-table hrl-table--stack">
69
- <thead><tr><th>Employee</th><th>Days</th><th>LOP</th><th>Gross</th><th>Deductions</th><th>Net</th><th></th></tr></thead>
72
+ <thead><tr><th>Employee</th><th>Paid days</th><th><abbr title="Loss of pay: unpaid days">LOP</abbr></th><th>Gross</th><th>Deductions</th><th>Net</th><th></th></tr></thead>
70
73
  <tbody>
71
74
  <% @slips.each do |slip| %>
72
75
  <tr>
73
76
  <td data-label="Employee"><%= hr_display_name(slip.user) %></td>
74
- <td data-label="Days" class="hrl-num"><%= slip.payable_days %></td>
75
- <td data-label="LOP" class="hrl-num">
76
- <%= slip.effective_lop_days %>
77
+ <td data-label="Paid days" class="hrl-num"><%= hrl_days(slip.payable_days) %></td>
78
+ <td data-label="Unpaid (LOP)" class="hrl-num">
79
+ <%= hrl_days(slip.effective_lop_days) %>
77
80
  <% if slip.lop_override %><span class="hrl-badge hrl-badge--warn">override</span><% end %>
78
81
  </td>
79
82
  <td data-label="Gross" class="hrl-num"><%= hrl_money(slip.gross_earnings) %></td>
80
83
  <td data-label="Deductions" class="hrl-num"><%= hrl_money(slip.total_deductions) %></td>
81
84
  <td data-label="Net" class="hrl-num"><strong><%= hrl_money(slip.net_pay) %></strong></td>
82
- <td data-label=""><a class="hrl-small" href="<%= hr_lite.admin_salary_slip_path(slip) %>">Detail</a></td>
85
+ <td data-label=""><a class="hrl-btn" href="<%= hr_lite.admin_salary_slip_path(slip) %>">Detail</a></td>
83
86
  </tr>
84
87
  <% end %>
85
88
  </tbody>
@@ -17,7 +17,7 @@
17
17
  <td data-label="Title"><a class="hrl-card__link" href="<%= hr_lite.policy_path(policy) %>"><%= policy.title %></a> <span class="hrl-badge hrl-badge--muted">v<%= policy.version %></span></td>
18
18
  <td data-label="Kind"><%= policy.announcement? ? "Announcement" : "Policy" %></td>
19
19
  <td data-label="In force"><%= policy.effective_from.strftime("%d %b %Y") %></td>
20
- <td data-label="Still to acknowledge"><%= policy.announcement? ? "—" : policy.outstanding_for.count %></td>
20
+ <td data-label="Still to acknowledge"><% if policy.announcement? %>—<% else %><% waiting = policy.outstanding_for.to_a %><details><summary class="hrl-btn"><%= pluralize(waiting.size, "person") %></summary><%= waiting.map { |u| hr_display_name(u) }.to_sentence %></details><% end %></td>
21
21
  </tr>
22
22
  <% end %>
23
23
  </tbody>
@@ -8,12 +8,12 @@
8
8
  <%= form_with model: @policy, url: hr_lite.admin_policies_path, method: :post, local: true do |f| %>
9
9
  <%= render "hr_lite/shared/form_errors", record: @policy %>
10
10
  <div class="hrl-field"><%= f.label :title %><%= f.text_field :title, required: true %></div>
11
- <div class="hrl-field"><%= f.label :body, "Text" %><%= f.text_area :body, rows: 10, required: true %></div>
12
- <div class="hrl-field"><%= f.label :effective_from, "In force from" %><%= f.date_field :effective_from %></div>
13
11
  <div class="hrl-field hrl-field--check">
14
12
  <%= f.check_box :acknowledgement_required %>
15
13
  <%= f.label :acknowledgement_required, "Policy — everyone must acknowledge it (untick for an announcement)" %>
16
14
  </div>
15
+ <div class="hrl-field"><%= f.label :body, "Text" %><%= f.text_area :body, rows: 10, required: true %></div>
16
+ <div class="hrl-field"><%= f.label :effective_from, "In force from" %><%= f.date_field :effective_from %></div>
17
17
  <div class="hrl-form-actions">
18
18
  <%= f.submit "Publish", class: "hrl-btn hrl-btn--primary",
19
19
  data: { turbo_confirm: "Publish and notify #{pluralize(HrLite.active_employees.size, 'person')} by email?" } %>
@@ -1,13 +1,12 @@
1
- <% content_for(:page_title) { "Regularization tickets" } %>
1
+ <% content_for(:page_title) { "Day fixes" } %>
2
2
  <div class="hrl-page__head">
3
- <h1 class="hrl-page__title">Regularization tickets</h1>
3
+ <h1 class="hrl-page__title">Day fixes</h1>
4
4
  </div>
5
5
 
6
6
  <div class="hrl-row hrl-row--mb">
7
7
  <span class="hrl-small hrl-muted">Status</span>
8
8
  <% HrLite::RegularizationRequest::STATUSES.each do |status| %>
9
- <a class="hrl-btn <%= 'hrl-btn--primary' if @status == status %>"
10
- href="<%= hr_lite.admin_regularization_requests_path(status: status) %>"><%= status.humanize %></a>
9
+ <%= link_to status.humanize, hr_lite.admin_regularization_requests_path(status: status), class: "hrl-btn", aria: { current: ("true" if @status == status) } %>
11
10
  <% end %>
12
11
  </div>
13
12
 
@@ -1,10 +1,7 @@
1
1
  <% content_for(:page_title) { "Ticket — #{hr_display_name(@request.user)}" } %>
2
+ <a class="hrl-backlink" href="<%= hr_lite.admin_regularization_requests_path %>"><span aria-hidden="true">‹</span> Approvals</a>
2
3
  <div class="hrl-page__head">
3
4
  <h1 class="hrl-page__title">Regularization — <%= hr_display_name(@request.user) %></h1>
4
- <div class="hrl-page__actions">
5
- <a class="hrl-btn" href="<%= hr_lite.admin_regularization_requests_path %>">All tickets</a>
6
- <a class="hrl-btn" href="<%= hr_lite.admin_attendance_path(@request.user_id, month: @request.date.strftime("%Y-%m")) %>">Their month</a>
7
- </div>
8
5
  </div>
9
6
 
10
7
  <section class="hrl-card">
@@ -28,6 +25,7 @@
28
25
  <% if @request.decision_note.present? %> · <%= @request.decision_note %><% end %></dd>
29
26
  <% end %>
30
27
  </dl>
28
+ <a class="hrl-card__link" href="<%= hr_lite.admin_attendance_path(@request.user_id, month: @request.date.strftime("%Y-%m")) %>">Their month</a>
31
29
  </section>
32
30
 
33
31
  <% if @request.pending? %>
@@ -38,7 +36,8 @@
38
36
  <%# Approve and reject are two separate forms on purpose. Rails binds the
39
37
  CSRF token to the form's OWN action, so one form retargeted with
40
38
  `formaction:` can never pass verification on the second endpoint. %>
41
- <%= form_with url: hr_lite.approve_admin_regularization_request_path(@request), method: :post, local: true do |f| %>
39
+ <%= form_with url: hr_lite.approve_admin_regularization_request_path(@request), method: :post, local: true,
40
+ html: { data: { turbo_confirm: "Write the proposed times onto #{hr_display_name(@request.user)}'s attendance for #{@request.date.strftime('%d %b %Y')}?" } } do |f| %>
42
41
  <div class="hrl-field">
43
42
  <%= f.label :decision_note, "Note (optional)", for: "approve_note" %>
44
43
  <%= f.text_area :decision_note, id: "approve_note" %>
@@ -9,7 +9,7 @@
9
9
  <%= form_with url: hr_lite.admin_reports_path, method: :get, local: true do %>
10
10
  <div class="hrl-field">
11
11
  <%= label_tag :month, "Month" %>
12
- <%= text_field_tag :month, @month.strftime("%Y-%m"), placeholder: "YYYY-MM" %>
12
+ <%= month_field_tag :month, @month.strftime("%Y-%m"), max: Date.current.strftime("%Y-%m"), class: "hrl-input" %>
13
13
  </div>
14
14
  <div class="hrl-form-actions">
15
15
  <%= submit_tag "Change month", class: "hrl-btn" %>
@@ -26,11 +26,8 @@
26
26
  <tr>
27
27
  <td data-label="Report"><%= label %></td>
28
28
  <td data-label="">
29
- <a class="hrl-small"
30
- href="<%= hr_lite.admin_report_path(name, month: @month.strftime("%Y-%m")) %>">Open</a>
31
- &nbsp;
32
- <a class="hrl-small"
33
- href="<%= hr_lite.admin_report_path(name, month: @month.strftime("%Y-%m"), format: :csv) %>">CSV</a>
29
+ <a class="hrl-btn" href="<%= hr_lite.admin_report_path(name, month: @month.strftime("%Y-%m")) %>">Open</a>
30
+ <a class="hrl-btn" href="<%= hr_lite.admin_report_path(name, month: @month.strftime("%Y-%m"), format: :csv) %>">CSV</a>
34
31
  </td>
35
32
  </tr>
36
33
  <% end %>
@@ -1,7 +1,21 @@
1
1
  <% content_for(:page_title) { @reports_title = HrLite::Admin::ReportsController::REPORTS[@name] } %>
2
+ <% content_for(:back) { "Reports" } %>
3
+ <%# The screen reads like the rest of the app; the CSV keeps raw values for spreadsheets. %>
4
+ <% heading = ->(h) { h == "LOP" ? "Unpaid days (LOP)" : h }
5
+ shown = lambda do |header, value|
6
+ if value.blank? then "—"
7
+ elsif header == "Amount" then hrl_money(value)
8
+ elsif header.in?([ "Joined", "Left", "Spent on" ]) then Date.parse(value.to_s).strftime("%d %b %Y")
9
+ elsif header == "Status" then value.to_s.humanize
10
+ elsif value.is_a?(Float) then hrl_days(value)
11
+ else value
12
+ end
13
+ end %>
2
14
  <div class="hrl-page__head">
3
15
  <h1 class="hrl-page__title"><%= HrLite::Admin::ReportsController::REPORTS[@name] %></h1>
4
16
  <div class="hrl-page__actions">
17
+ <a class="hrl-btn" href="<%= hr_lite.admin_report_path(@name, month: @month.prev_month.strftime("%Y-%m")) %>">‹ <%= @month.prev_month.strftime("%b %Y") %></a>
18
+ <% if @month.next_month <= Date.current %><a class="hrl-btn" href="<%= hr_lite.admin_report_path(@name, month: @month.next_month.strftime("%Y-%m")) %>"><%= @month.next_month.strftime("%b %Y") %> ›</a><% end %>
5
19
  <a class="hrl-btn"
6
20
  href="<%= hr_lite.admin_report_path(@name, month: @month.strftime("%Y-%m"), format: :csv) %>">Download CSV</a>
7
21
  </div>
@@ -15,13 +29,13 @@
15
29
  <div class="hrl-table-wrap">
16
30
  <table class="hrl-table hrl-table--stack">
17
31
  <thead>
18
- <tr><% @rows.first.keys.each do |header| %><th><%= header %></th><% end %></tr>
32
+ <tr><% @rows.first.keys.each do |header| %><th><%= heading.(header) %></th><% end %></tr>
19
33
  </thead>
20
34
  <tbody>
21
35
  <% @rows.each do |row| %>
22
36
  <tr>
23
37
  <% row.each do |header, value| %>
24
- <td data-label="<%= header %>"><%= value %></td>
38
+ <td data-label="<%= heading.(header) %>"><%= shown.(header, value) %></td>
25
39
  <% end %>
26
40
  </tr>
27
41
  <% end %>
@@ -7,8 +7,8 @@
7
7
  <% if role.system? %>
8
8
  <%= f.text_field :name, disabled: true %>
9
9
  <span class="hrl-small hrl-muted">
10
- Built-in roles keep their name — the upgrade path and the seed both
11
- identify them by it. The permissions below are yours to change.
10
+ Built-in roles keep their name, because the system finds them by it.
11
+ The permissions below are yours to change.
12
12
  </span>
13
13
  <% else %>
14
14
  <%= f.text_field :name %>
@@ -20,8 +20,9 @@
20
20
  </div>
21
21
 
22
22
  <% HrLite::Permissions.grouped.each do |group, keys| %>
23
- <fieldset>
24
- <legend><%= group %></legend>
23
+ <% granted = keys.count { |k| (role.grant_map[k] || "none") != "none" } %>
24
+ <details class="hrl-card" <%= "open" if granted.positive? %>>
25
+ <summary class="hrl-btn hrl-btn--block"><%= group %> · <%= granted %> of <%= keys.size %> granted</summary>
25
26
  <% keys.each do |key| %>
26
27
  <% held = role.grant_map[key] || "none" %>
27
28
  <div class="hrl-field">
@@ -36,7 +37,7 @@
36
37
  id: "grants_#{key.tr('.', '_')}" %>
37
38
  </div>
38
39
  <% end %>
39
- </fieldset>
40
+ </details>
40
41
  <% end %>
41
42
 
42
43
  <div class="hrl-form-actions">
@@ -11,8 +11,9 @@
11
11
  <section class="hrl-card">
12
12
  <h2 class="hrl-card__title">Tax working</h2>
13
13
  <dl class="hrl-deflist">
14
- <% @slip.tax_details_hash.each do |key, value| %>
15
- <dt><%= key.humanize %></dt><dd><%= value %></dd>
14
+ <% @slip.tax_details_hash.except("high_income_review").each do |key, value| %>
15
+ <dt><%= key == "fy_tds_already_deducted" ? "TDS already deducted this year" : key.humanize %></dt>
16
+ <dd><%= key.in?(%w[regime months_remaining]) ? value.to_s.humanize : hrl_money(value) %></dd>
16
17
  <% end %>
17
18
  </dl>
18
19
  <% if @slip.tax_details_hash["high_income_review"] == "true" %>
@@ -5,6 +5,7 @@
5
5
  <div class="hrl-field">
6
6
  <%= f.label :annual_ctc, "Annual CTC (₹ a year)" %>
7
7
  <%= f.number_field :annual_ctc, step: 1, min: 1, required: true, value: (structure.annual_ctc || breakup&.dig(:annual_ctc))&.round %>
8
+ <% if structure.annual_ctc %><p class="hrl-hint">= <%= hrl_money(structure.annual_ctc) %> a year</p><% end %>
8
9
  <p class="hrl-hint">The only amount you type. Saving splits it: Basic 50%, HRA 50% of Basic in a metro (40% elsewhere), employer PF, ESI and gratuity (4.81% of Basic) inside the CTC, the rest as special allowance.</p>
9
10
  </div>
10
11
  <div class="hrl-field">
@@ -6,11 +6,13 @@
6
6
  <section class="hrl-card">
7
7
  <%= form_with model: @setting, url: hr_lite.admin_setting_path, method: :patch, local: true do |f| %>
8
8
  <%= render "hr_lite/shared/form_errors", record: @setting %>
9
+ <h2 class="hrl-card__title">Working week</h2>
9
10
  <div class="hrl-field">
10
11
  <%= f.label :weekend_policy, "Weekly off policy" %>
11
- <%= f.select :weekend_policy, HrLite::Setting::WEEKEND_POLICIES.map { |p| [ p.humanize, p ] } %>
12
+ <%= f.select :weekend_policy, { "Sunday only" => "sun_only", "Saturday and Sunday" => "sat_sun", "Sunday, 2nd and 4th Saturday" => "second_fourth_sat_sun" }.select { |_, v| v.in?(HrLite::Setting::WEEKEND_POLICIES) } %>
12
13
  <p class="hrl-hint">Drives working-day math everywhere: leave counting, attendance, payroll.</p>
13
14
  </div>
15
+ <h2 class="hrl-card__title hrl-mt">Breaks</h2>
14
16
  <div class="hrl-field">
15
17
  <%= f.label :lunch_break_minutes, "Lunch break limit (minutes)" %>
16
18
  <%= f.number_field :lunch_break_minutes, min: 5, max: 120 %>
@@ -20,6 +22,7 @@
20
22
  <%= f.label :short_break_minutes, "Short break limit (minutes)" %>
21
23
  <%= f.number_field :short_break_minutes, min: 1, max: 60 %>
22
24
  </div>
25
+ <h2 class="hrl-card__title hrl-mt">Working day</h2>
23
26
  <div class="hrl-field">
24
27
  <%= f.label :day_minutes, "Working day (minutes, break included)" %>
25
28
  <%= f.number_field :day_minutes, min: 240, max: 720, step: 15 %>
@@ -39,6 +42,7 @@
39
42
  <%= f.time_field :recheck_in_until %>
40
43
  <p class="hrl-hint">After checking out, a person can check in again once a day inside these hours.</p>
41
44
  </div>
45
+ <h2 class="hrl-card__title hrl-mt">Penalties and codes</h2>
42
46
  <div class="hrl-field">
43
47
  <%= f.label :penalty_leave_type_id, "Penalties come off" %>
44
48
  <%= f.collection_select :penalty_leave_type_id, HrLite::LeaveType.active, :id, :name, { include_blank: "No leave (penalties stay as time)" }, data: { hrl_filter: "Type a leave type to filter" } %>
@@ -18,8 +18,8 @@
18
18
 
19
19
  <fieldset>
20
20
  <legend>Provident fund</legend>
21
- <% { employee_rate: "Employee rate (0.12 = 12%)", employer_rate: "Employer rate",
22
- eps_rate: "EPS rate", wage_ceiling: "PF wage ceiling (₹)",
21
+ <% { employee_rate: "Employee rate (0.12 = 12%)", employer_rate: "Employer rate (0.12 = 12%)",
22
+ eps_rate: "EPS rate (0.0833 = 8.33%)", wage_ceiling: "PF wage ceiling (₹)",
23
23
  eps_wage_ceiling: "EPS wage ceiling (₹)", edli_rate: "EDLI rate",
24
24
  edli_ceiling: "EDLI ceiling (₹)", admin_rate: "PF admin rate" }.each do |key, label| %>
25
25
  <div class="hrl-field">
@@ -32,7 +32,7 @@
32
32
 
33
33
  <fieldset>
34
34
  <legend>ESI</legend>
35
- <% { employee_rate: "Employee rate", employer_rate: "Employer rate",
35
+ <% { employee_rate: "Employee rate (0.0075 = 0.75%)", employer_rate: "Employer rate (0.0325 = 3.25%)",
36
36
  gross_ceiling: "Gross ceiling (₹)" }.each do |key, label| %>
37
37
  <div class="hrl-field">
38
38
  <%= label_tag "statutory_rate_card_esi_#{key}", label %>
@@ -16,10 +16,12 @@
16
16
  <table class="hrl-table hrl-table--stack">
17
17
  <thead><tr><th>Year</th><th>PF ceiling</th><th>ESI ceiling</th><th>Verified</th><th></th></tr></thead>
18
18
  <tbody>
19
+ <% live = HrLite::StatutoryRateCardRecord.effective_for(Date.current.beginning_of_month) %>
19
20
  <% @cards.each do |card| %>
20
21
  <tr>
21
22
  <td data-label="Year">
22
23
  <strong>FY <%= card.financial_year %></strong>
24
+ <% if card == live %><span class="hrl-badge hrl-badge--ok">In force</span><% end %>
23
25
  <div class="hrl-small hrl-muted">from <%= card.effective_from.strftime("%d %b %Y") %></div>
24
26
  </td>
25
27
  <td data-label="PF ceiling" class="hrl-num"><%= HrLite::Money.format(card.to_card[:pf][:wage_ceiling]) %></td>
@@ -33,7 +35,7 @@
33
35
  <% end %>
34
36
  </td>
35
37
  <td data-label="">
36
- <a class="hrl-small" href="<%= hr_lite.edit_admin_statutory_rate_card_path(card) %>">Edit</a>
38
+ <a class="hrl-btn" href="<%= hr_lite.edit_admin_statutory_rate_card_path(card) %>">Edit</a>
37
39
  </td>
38
40
  </tr>
39
41
  <% end %>
@@ -26,7 +26,7 @@
26
26
  <td data-label="Regime"><%= declaration.regime.humanize %></td>
27
27
  <td data-label="Claimed" class="hrl-num"><%= hrl_money(declaration.declared_total) %></td>
28
28
  <td data-label="">
29
- <a class="hrl-small" href="<%= hr_lite.admin_tax_declaration_path(declaration) %>">Check</a>
29
+ <a class="hrl-btn" href="<%= hr_lite.admin_tax_declaration_path(declaration) %>">Check</a>
30
30
  </td>
31
31
  </tr>
32
32
  <% end %>
@@ -53,6 +53,7 @@
53
53
  </section>
54
54
  <% end %>
55
55
 
56
+ <% if @declaration.submitted? %>
56
57
  <section class="hrl-card">
57
58
  <h2 class="hrl-card__title">Decide</h2>
58
59
  <div class="hrl-row">
@@ -71,3 +72,4 @@
71
72
  <%= submit_tag "Send back", class: "hrl-btn" %>
72
73
  <% end %>
73
74
  </section>
75
+ <% end %>
@@ -3,7 +3,22 @@
3
3
  <h1 class="hrl-page__title">Work from home</h1>
4
4
  </div>
5
5
 
6
- <section class="hrl-card">
6
+ <% if @grantable.any? %>
7
+ <section class="hrl-card">
8
+ <h2 class="hrl-card__title">Give WFH</h2>
9
+ <p class="hrl-small hrl-muted">Approved straight away, up to <%= HrLite::WfhRequest::HR_MAX_DAYS %> days from today or later.</p>
10
+ <%= form_with url: hr_lite.admin_wfh_requests_path, method: :post, local: true do %>
11
+ <div class="hrl-field"><label for="grant-user">Employee</label><%= select_tag :user_id, options_for_select(@grantable.map { |u| [ hr_display_name(u), u.id ] }), include_blank: "Choose one", required: true, id: "grant-user", data: { hrl_filter: ("Type a name to filter" if @grantable.size > 5) } %></div>
12
+ <div class="hrl-field"><label for="grant-from">From</label><%= date_field_tag :start_date, Date.current, min: Date.current, required: true, id: "grant-from" %></div>
13
+ <div class="hrl-field"><label for="grant-to">To</label><%= date_field_tag :end_date, Date.current, min: Date.current, required: true, id: "grant-to" %></div>
14
+ <div class="hrl-field"><label for="grant-reason">Reason</label><%= text_area_tag :reason, nil, rows: 2, required: true, id: "grant-reason" %></div>
15
+ <div class="hrl-form-actions"><button type="submit" class="hrl-btn hrl-btn--primary">Give WFH</button></div>
16
+ <% end %>
17
+ </section>
18
+ <% end %>
19
+
20
+ <details class="hrl-card"<%= " open" if request.query_string.present? %>>
21
+ <summary class="hrl-btn hrl-btn--block">Filter</summary>
7
22
  <%= form_with url: hr_lite.admin_wfh_requests_path, method: :get, local: true do %>
8
23
  <div class="hrl-field"><label for="wfh-from">From</label><%= date_field_tag :from, @from, id: "wfh-from" %></div>
9
24
  <div class="hrl-field"><label for="wfh-to">To</label><%= date_field_tag :to, @to, id: "wfh-to" %></div>
@@ -19,7 +34,7 @@
19
34
  <a class="hrl-btn" href="<%= hr_lite.admin_wfh_requests_path %>">Reset</a>
20
35
  </div>
21
36
  <% end %>
22
- </section>
37
+ </details>
23
38
 
24
39
  <div class="hrl-kpis">
25
40
  <div class="hrl-kpi"><div class="hrl-kpi__value"><%= @kpis[:requested] %></div><div class="hrl-kpi__label">Employees requested</div></div>
@@ -48,20 +63,6 @@
48
63
  <% end %>
49
64
  </section>
50
65
 
51
- <% if @grantable.any? %>
52
- <section class="hrl-card">
53
- <h2 class="hrl-card__title">Give WFH</h2>
54
- <p class="hrl-small hrl-muted">Approved straight away, up to <%= HrLite::WfhRequest::HR_MAX_DAYS %> days from today or later.</p>
55
- <%= form_with url: hr_lite.admin_wfh_requests_path, method: :post, local: true do %>
56
- <div class="hrl-field"><label for="grant-user">Employee</label><%= select_tag :user_id, options_for_select(@grantable.map { |u| [ hr_display_name(u), u.id ] }), include_blank: "Choose one", required: true, id: "grant-user", data: { hrl_filter: ("Type a name to filter" if @grantable.size > 5) } %></div>
57
- <div class="hrl-field"><label for="grant-from">From</label><%= date_field_tag :start_date, Date.current, min: Date.current, required: true, id: "grant-from" %></div>
58
- <div class="hrl-field"><label for="grant-to">To</label><%= date_field_tag :end_date, Date.current, min: Date.current, required: true, id: "grant-to" %></div>
59
- <div class="hrl-field"><label for="grant-reason">Reason</label><%= text_area_tag :reason, nil, rows: 2, required: true, id: "grant-reason" %></div>
60
- <div class="hrl-form-actions"><button type="submit" class="hrl-btn hrl-btn--primary">Give WFH</button></div>
61
- <% end %>
62
- </section>
63
- <% end %>
64
-
65
66
  <section class="hrl-card">
66
67
  <h2 class="hrl-card__title">Schedules <%= @from.strftime("%-d %b") %> – <%= @to.strftime("%-d %b") %></h2>
67
68
  <% if @requests.any? %>