hr_lite 0.1.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 +7 -0
- data/CHANGELOG.md +33 -0
- data/MIT-LICENSE +20 -0
- data/README.md +166 -0
- data/Rakefile +3 -0
- data/app/assets/javascripts/hr_lite/geo_punch.js +61 -0
- data/app/assets/javascripts/hr_lite/mention.js +100 -0
- data/app/assets/stylesheets/hr_lite/application.css +15 -0
- data/app/assets/stylesheets/hr_lite/hr_lite.css +239 -0
- data/app/controllers/hr_lite/admin/appraisals_controller.rb +74 -0
- data/app/controllers/hr_lite/admin/attendances_controller.rb +79 -0
- data/app/controllers/hr_lite/admin/audit_logs_controller.rb +9 -0
- data/app/controllers/hr_lite/admin/base_controller.rb +16 -0
- data/app/controllers/hr_lite/admin/designation_changes_controller.rb +33 -0
- data/app/controllers/hr_lite/admin/employees_controller.rb +53 -0
- data/app/controllers/hr_lite/admin/holidays_controller.rb +70 -0
- data/app/controllers/hr_lite/admin/leadership_controller.rb +18 -0
- data/app/controllers/hr_lite/admin/leave_balances_controller.rb +41 -0
- data/app/controllers/hr_lite/admin/leave_requests_controller.rb +41 -0
- data/app/controllers/hr_lite/admin/leave_types_controller.rb +54 -0
- data/app/controllers/hr_lite/admin/office_locations_controller.rb +46 -0
- data/app/controllers/hr_lite/admin/overview_controller.rb +12 -0
- data/app/controllers/hr_lite/admin/payroll_runs_controller.rb +93 -0
- data/app/controllers/hr_lite/admin/salary_slips_controller.rb +44 -0
- data/app/controllers/hr_lite/admin/salary_structures_controller.rb +49 -0
- data/app/controllers/hr_lite/admin/settings_controller.rb +18 -0
- data/app/controllers/hr_lite/application_controller.rb +76 -0
- data/app/controllers/hr_lite/appraisals_controller.rb +18 -0
- data/app/controllers/hr_lite/attendance_controller.rb +37 -0
- data/app/controllers/hr_lite/calendar_controller.rb +20 -0
- data/app/controllers/hr_lite/career_controller.rb +9 -0
- data/app/controllers/hr_lite/employee_profiles_controller.rb +9 -0
- data/app/controllers/hr_lite/holidays_controller.rb +9 -0
- data/app/controllers/hr_lite/home_controller.rb +7 -0
- data/app/controllers/hr_lite/kudos_controller.rb +36 -0
- data/app/controllers/hr_lite/leave_balances_controller.rb +11 -0
- data/app/controllers/hr_lite/leave_requests_controller.rb +56 -0
- data/app/controllers/hr_lite/salary_slips_controller.rb +39 -0
- data/app/controllers/hr_lite/users_controller.rb +9 -0
- data/app/helpers/hr_lite/application_helper.rb +105 -0
- data/app/jobs/hr_lite/application_job.rb +4 -0
- data/app/jobs/hr_lite/daily_digest_job.rb +33 -0
- data/app/jobs/hr_lite/leave_year_rollover_job.rb +29 -0
- data/app/mailers/hr_lite/application_mailer.rb +5 -0
- data/app/mailers/hr_lite/event_mailer.rb +33 -0
- data/app/models/concerns/hr_lite/audited.rb +70 -0
- data/app/models/concerns/hr_lite/encrypted_money.rb +32 -0
- data/app/models/hr_lite/application_record.rb +5 -0
- data/app/models/hr_lite/appraisal.rb +89 -0
- data/app/models/hr_lite/attendance_record.rb +41 -0
- data/app/models/hr_lite/audit_log.rb +17 -0
- data/app/models/hr_lite/designation_change.rb +52 -0
- data/app/models/hr_lite/employee_profile.rb +72 -0
- data/app/models/hr_lite/holiday.rb +18 -0
- data/app/models/hr_lite/kudo.rb +54 -0
- data/app/models/hr_lite/kudo_mention.rb +8 -0
- data/app/models/hr_lite/leave_balance.rb +42 -0
- data/app/models/hr_lite/leave_request.rb +207 -0
- data/app/models/hr_lite/leave_type.rb +22 -0
- data/app/models/hr_lite/office_location.rb +21 -0
- data/app/models/hr_lite/payroll_run.rb +112 -0
- data/app/models/hr_lite/salary_slip.rb +78 -0
- data/app/models/hr_lite/salary_structure.rb +49 -0
- data/app/models/hr_lite/setting.rb +14 -0
- data/app/services/hr_lite/attendance_puncher.rb +103 -0
- data/app/services/hr_lite/attendance_summary.rb +98 -0
- data/app/services/hr_lite/calculators/esi.rb +25 -0
- data/app/services/hr_lite/calculators/pf.rb +32 -0
- data/app/services/hr_lite/calculators/professional_tax.rb +20 -0
- data/app/services/hr_lite/calculators/proration.rb +30 -0
- data/app/services/hr_lite/calculators/tds.rb +74 -0
- data/app/services/hr_lite/day_status.rb +49 -0
- data/app/services/hr_lite/leave_day_counter.rb +24 -0
- data/app/services/hr_lite/overview_query.rb +39 -0
- data/app/services/hr_lite/payroll_run_processor.rb +46 -0
- data/app/services/hr_lite/pdf_renderer.rb +23 -0
- data/app/services/hr_lite/slip_builder.rb +106 -0
- data/app/services/hr_lite/working_calendar.rb +41 -0
- data/app/views/hr_lite/admin/appraisals/_form.html.erb +29 -0
- data/app/views/hr_lite/admin/appraisals/edit.html.erb +6 -0
- data/app/views/hr_lite/admin/appraisals/new.html.erb +6 -0
- data/app/views/hr_lite/admin/attendances/index.html.erb +49 -0
- data/app/views/hr_lite/admin/attendances/show.html.erb +55 -0
- data/app/views/hr_lite/admin/audit_logs/index.html.erb +39 -0
- data/app/views/hr_lite/admin/designation_changes/new.html.erb +19 -0
- data/app/views/hr_lite/admin/employees/_form.html.erb +40 -0
- data/app/views/hr_lite/admin/employees/edit.html.erb +3 -0
- data/app/views/hr_lite/admin/employees/index.html.erb +40 -0
- data/app/views/hr_lite/admin/employees/new.html.erb +3 -0
- data/app/views/hr_lite/admin/employees/show.html.erb +75 -0
- data/app/views/hr_lite/admin/holidays/index.html.erb +59 -0
- data/app/views/hr_lite/admin/leave_balances/index.html.erb +58 -0
- data/app/views/hr_lite/admin/leave_requests/index.html.erb +35 -0
- data/app/views/hr_lite/admin/leave_requests/show.html.erb +39 -0
- data/app/views/hr_lite/admin/leave_types/_form.html.erb +27 -0
- data/app/views/hr_lite/admin/leave_types/edit.html.erb +10 -0
- data/app/views/hr_lite/admin/leave_types/index.html.erb +39 -0
- data/app/views/hr_lite/admin/leave_types/new.html.erb +3 -0
- data/app/views/hr_lite/admin/office_locations/_form.html.erb +18 -0
- data/app/views/hr_lite/admin/office_locations/edit.html.erb +10 -0
- data/app/views/hr_lite/admin/office_locations/index.html.erb +30 -0
- data/app/views/hr_lite/admin/office_locations/new.html.erb +3 -0
- data/app/views/hr_lite/admin/overview/index.html.erb +72 -0
- data/app/views/hr_lite/admin/payroll_runs/index.html.erb +33 -0
- data/app/views/hr_lite/admin/payroll_runs/new.html.erb +20 -0
- data/app/views/hr_lite/admin/payroll_runs/show.html.erb +76 -0
- data/app/views/hr_lite/admin/salary_slips/show.html.erb +42 -0
- data/app/views/hr_lite/admin/salary_structures/_form.html.erb +25 -0
- data/app/views/hr_lite/admin/salary_structures/edit.html.erb +6 -0
- data/app/views/hr_lite/admin/salary_structures/new.html.erb +6 -0
- data/app/views/hr_lite/admin/settings/edit.html.erb +18 -0
- data/app/views/hr_lite/appraisals/index.html.erb +22 -0
- data/app/views/hr_lite/appraisals/show.html.erb +32 -0
- data/app/views/hr_lite/attendance/_month_grid.html.erb +41 -0
- data/app/views/hr_lite/attendance/_punch_card.html.erb +41 -0
- data/app/views/hr_lite/attendance/show.html.erb +22 -0
- data/app/views/hr_lite/calendar/show.html.erb +45 -0
- data/app/views/hr_lite/career/show.html.erb +49 -0
- data/app/views/hr_lite/employee_profiles/show.html.erb +22 -0
- data/app/views/hr_lite/event_mailer/event.html.erb +10 -0
- data/app/views/hr_lite/event_mailer/event.text.erb +10 -0
- data/app/views/hr_lite/event_mailer/leadership.html.erb +27 -0
- data/app/views/hr_lite/event_mailer/leadership.text.erb +16 -0
- data/app/views/hr_lite/holidays/_next_holiday.html.erb +12 -0
- data/app/views/hr_lite/holidays/index.html.erb +30 -0
- data/app/views/hr_lite/home/index.html.erb +25 -0
- data/app/views/hr_lite/kudos/_kudo.html.erb +17 -0
- data/app/views/hr_lite/kudos/index.html.erb +39 -0
- data/app/views/hr_lite/leave_balances/index.html.erb +19 -0
- data/app/views/hr_lite/leave_requests/_balance_chips.html.erb +17 -0
- data/app/views/hr_lite/leave_requests/index.html.erb +37 -0
- data/app/views/hr_lite/leave_requests/new.html.erb +36 -0
- data/app/views/hr_lite/leave_requests/show.html.erb +25 -0
- data/app/views/hr_lite/salary_slips/_slip_detail.html.erb +48 -0
- data/app/views/hr_lite/salary_slips/index.html.erb +29 -0
- data/app/views/hr_lite/salary_slips/pdf.html.erb +72 -0
- data/app/views/hr_lite/salary_slips/show.html.erb +12 -0
- data/app/views/hr_lite/shared/_form_errors.html.erb +10 -0
- data/app/views/hr_lite/shared/_pagination.html.erb +11 -0
- data/app/views/layouts/hr_lite/application.html.erb +56 -0
- data/app/views/layouts/hr_lite/mailer.html.erb +30 -0
- data/app/views/layouts/hr_lite/mailer.text.erb +6 -0
- data/app/views/layouts/hr_lite/pdf.html.erb +26 -0
- data/config/routes.rb +57 -0
- data/db/migrate/20260718190726_create_hr_lite_kudos.rb +14 -0
- data/db/migrate/20260718190727_create_hr_lite_kudo_mentions.rb +12 -0
- data/db/migrate/20260718190728_create_hr_lite_audit_logs.rb +15 -0
- data/db/migrate/20260718192342_create_hr_lite_office_locations.rb +13 -0
- data/db/migrate/20260718192343_create_hr_lite_attendance_records.rb +27 -0
- data/db/migrate/20260718193045_create_hr_lite_holidays.rb +14 -0
- data/db/migrate/20260718193046_create_hr_lite_leave_types.rb +20 -0
- data/db/migrate/20260718193047_create_hr_lite_leave_balances.rb +17 -0
- data/db/migrate/20260718193048_create_hr_lite_leave_requests.rb +23 -0
- data/db/migrate/20260718193049_create_hr_lite_settings.rb +10 -0
- data/db/migrate/20260718194534_create_hr_lite_employee_profiles.rb +27 -0
- data/db/migrate/20260718194535_create_hr_lite_salary_structures.rb +23 -0
- data/db/migrate/20260718194536_create_hr_lite_payroll_runs.rb +20 -0
- data/db/migrate/20260718194537_create_hr_lite_salary_slips.rb +28 -0
- data/db/migrate/20260718195953_create_hr_lite_appraisals.rb +22 -0
- data/db/migrate/20260718195954_create_hr_lite_designation_changes.rb +17 -0
- data/lib/generators/hr_lite/install/install_generator.rb +29 -0
- data/lib/generators/hr_lite/install/templates/AFTER_INSTALL +35 -0
- data/lib/generators/hr_lite/install/templates/initializer.rb +45 -0
- data/lib/hr_lite/configuration.rb +60 -0
- data/lib/hr_lite/current.rb +7 -0
- data/lib/hr_lite/engine.rb +39 -0
- data/lib/hr_lite/geo.rb +22 -0
- data/lib/hr_lite/mention_parser.rb +18 -0
- data/lib/hr_lite/money.rb +33 -0
- data/lib/hr_lite/notifications.rb +99 -0
- data/lib/hr_lite/seeds.rb +42 -0
- data/lib/hr_lite/statutory_rate_card.rb +60 -0
- data/lib/hr_lite/version.rb +3 -0
- data/lib/hr_lite.rb +104 -0
- data/lib/tasks/hr_lite_tasks.rake +8 -0
- metadata +250 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<section class="hrl-card">
|
|
2
|
+
<%= form_with model: office_location, url: url, method: method, local: true do |f| %>
|
|
3
|
+
<%= render "hr_lite/shared/form_errors", record: office_location %>
|
|
4
|
+
<div class="hrl-field"><%= f.label :name %><%= f.text_field :name %></div>
|
|
5
|
+
<div class="hrl-field">
|
|
6
|
+
<%= f.label :lat, "Latitude" %>
|
|
7
|
+
<%= f.number_field :lat, step: 0.000001 %>
|
|
8
|
+
<p class="hrl-hint">Right-click the office on Google Maps and copy the coordinates.</p>
|
|
9
|
+
</div>
|
|
10
|
+
<div class="hrl-field"><%= f.label :lng, "Longitude" %><%= f.number_field :lng, step: 0.000001 %></div>
|
|
11
|
+
<div class="hrl-field"><%= f.label :radius_m, "Radius (metres)" %><%= f.number_field :radius_m %></div>
|
|
12
|
+
<div class="hrl-field hrl-field--check"><%= f.check_box :active %><%= f.label :active %></div>
|
|
13
|
+
<div class="hrl-form-actions">
|
|
14
|
+
<%= f.submit "Save", class: "hrl-btn hrl-btn--primary" %>
|
|
15
|
+
<a class="hrl-btn" href="<%= hr_lite.admin_office_locations_path %>">Cancel</a>
|
|
16
|
+
</div>
|
|
17
|
+
<% end %>
|
|
18
|
+
</section>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<% content_for(:page_title) { "Edit office" } %>
|
|
2
|
+
<div class="hrl-page__head">
|
|
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,
|
|
6
|
+
class: "hrl-btn hrl-btn--danger",
|
|
7
|
+
form: { data: { turbo_confirm: "Remove this office?" } } %>
|
|
8
|
+
</div>
|
|
9
|
+
</div>
|
|
10
|
+
<%= render "form", office_location: @office_location, url: hr_lite.admin_office_location_path(@office_location), method: :patch %>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<% content_for(:page_title) { "Office locations" } %>
|
|
2
|
+
<div class="hrl-page__head">
|
|
3
|
+
<h1 class="hrl-page__title">Office locations</h1>
|
|
4
|
+
<div class="hrl-page__actions">
|
|
5
|
+
<a class="hrl-btn hrl-btn--primary" href="<%= hr_lite.new_admin_office_location_path %>">Add office</a>
|
|
6
|
+
</div>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<section class="hrl-card">
|
|
10
|
+
<% if @office_locations.any? %>
|
|
11
|
+
<div class="hrl-table-wrap">
|
|
12
|
+
<table class="hrl-table hrl-table--stack">
|
|
13
|
+
<thead><tr><th>Name</th><th>Coordinates</th><th>Radius</th><th>Active</th><th></th></tr></thead>
|
|
14
|
+
<tbody>
|
|
15
|
+
<% @office_locations.each do |office| %>
|
|
16
|
+
<tr>
|
|
17
|
+
<td data-label="Name"><%= office.name %></td>
|
|
18
|
+
<td data-label="Coordinates"><%= office.lat %>, <%= office.lng %></td>
|
|
19
|
+
<td data-label="Radius" class="hrl-num"><%= office.radius_m %> m</td>
|
|
20
|
+
<td data-label="Active"><%= office.active ? "Yes" : "No" %></td>
|
|
21
|
+
<td data-label=""><a class="hrl-small" href="<%= hr_lite.edit_admin_office_location_path(office) %>">Edit</a></td>
|
|
22
|
+
</tr>
|
|
23
|
+
<% end %>
|
|
24
|
+
</tbody>
|
|
25
|
+
</table>
|
|
26
|
+
</div>
|
|
27
|
+
<% else %>
|
|
28
|
+
<p class="hrl-card--empty">No offices yet — punches will never be radius-flagged until one exists.</p>
|
|
29
|
+
<% end %>
|
|
30
|
+
</section>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<% content_for(:page_title) { "HR overview" } %>
|
|
2
|
+
<div class="hrl-page__head">
|
|
3
|
+
<h1 class="hrl-page__title">HR overview</h1>
|
|
4
|
+
</div>
|
|
5
|
+
|
|
6
|
+
<div class="hrl-kpis">
|
|
7
|
+
<div class="hrl-kpi"><div class="hrl-kpi__value"><%= @kpis[:pending] %></div><div class="hrl-kpi__label">Pending approvals</div></div>
|
|
8
|
+
<div class="hrl-kpi"><div class="hrl-kpi__value"><%= @kpis[:on_leave] %></div><div class="hrl-kpi__label">On leave today</div></div>
|
|
9
|
+
<div class="hrl-kpi"><div class="hrl-kpi__value"><%= @kpis[:flagged] %></div><div class="hrl-kpi__label">Flagged punches</div></div>
|
|
10
|
+
<div class="hrl-kpi"><div class="hrl-kpi__value"><%= @kpis[:missing_checkout] %></div><div class="hrl-kpi__label">Missing checkouts</div></div>
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<% if @query.empty? %>
|
|
14
|
+
<p class="hrl-card hrl-card--empty">All present and accounted for.</p>
|
|
15
|
+
<% else %>
|
|
16
|
+
<% if @kpis[:pending].positive? %>
|
|
17
|
+
<section class="hrl-card">
|
|
18
|
+
<h2 class="hrl-card__title">Pending approvals</h2>
|
|
19
|
+
<% @query.pending_requests.limit(HrLite::Admin::OverviewController::SECTION_CAP).each do |request| %>
|
|
20
|
+
<div class="hrl-feed__item">
|
|
21
|
+
<div class="hrl-feed__body">
|
|
22
|
+
<strong><%= hr_display_name(request.user) %></strong>
|
|
23
|
+
— <%= request.leave_type.name %>, <%= request.date_range_label %> (<%= request.days_count.to_f %> d)
|
|
24
|
+
</div>
|
|
25
|
+
<a class="hrl-btn" href="<%= hr_lite.admin_leave_request_path(request) %>">Decide</a>
|
|
26
|
+
</div>
|
|
27
|
+
<% end %>
|
|
28
|
+
<a class="hrl-small" href="<%= hr_lite.admin_leave_requests_path %>">All requests</a>
|
|
29
|
+
</section>
|
|
30
|
+
<% end %>
|
|
31
|
+
|
|
32
|
+
<% if @kpis[:on_leave].positive? %>
|
|
33
|
+
<section class="hrl-card">
|
|
34
|
+
<h2 class="hrl-card__title">Out today</h2>
|
|
35
|
+
<% @query.on_leave_today.limit(HrLite::Admin::OverviewController::SECTION_CAP).each do |request| %>
|
|
36
|
+
<div class="hrl-feed__item">
|
|
37
|
+
<div class="hrl-feed__body">
|
|
38
|
+
<strong><%= hr_display_name(request.user) %></strong>
|
|
39
|
+
— <%= request.leave_type.name %> until <%= request.end_date.strftime("%d %b") %>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
<% end %>
|
|
43
|
+
</section>
|
|
44
|
+
<% end %>
|
|
45
|
+
|
|
46
|
+
<% if @kpis[:flagged].positive? %>
|
|
47
|
+
<section class="hrl-card">
|
|
48
|
+
<h2 class="hrl-card__title">Flagged punches today</h2>
|
|
49
|
+
<% @query.flagged_today.limit(HrLite::Admin::OverviewController::SECTION_CAP).each do |record| %>
|
|
50
|
+
<div class="hrl-feed__item">
|
|
51
|
+
<div class="hrl-feed__body">
|
|
52
|
+
<strong><%= hr_display_name(record.user) %></strong>
|
|
53
|
+
<span class="hrl-small hrl-muted"><%= record.flag_note %></span>
|
|
54
|
+
</div>
|
|
55
|
+
<a class="hrl-btn" href="<%= hr_lite.admin_attendance_path(record.user_id, date: record.date, month: record.date.strftime("%Y-%m")) %>">Review</a>
|
|
56
|
+
</div>
|
|
57
|
+
<% end %>
|
|
58
|
+
</section>
|
|
59
|
+
<% end %>
|
|
60
|
+
|
|
61
|
+
<% if @kpis[:missing_checkout].positive? %>
|
|
62
|
+
<section class="hrl-card">
|
|
63
|
+
<h2 class="hrl-card__title">Missing checkouts (yesterday)</h2>
|
|
64
|
+
<% @query.missing_checkout_yesterday.limit(HrLite::Admin::OverviewController::SECTION_CAP).each do |record| %>
|
|
65
|
+
<div class="hrl-feed__item">
|
|
66
|
+
<div class="hrl-feed__body"><strong><%= hr_display_name(record.user) %></strong></div>
|
|
67
|
+
<a class="hrl-btn" href="<%= hr_lite.admin_attendance_path(record.user_id, date: record.date, month: record.date.strftime("%Y-%m")) %>">Fix</a>
|
|
68
|
+
</div>
|
|
69
|
+
<% end %>
|
|
70
|
+
</section>
|
|
71
|
+
<% end %>
|
|
72
|
+
<% end %>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<% content_for(:page_title) { "Payroll" } %>
|
|
2
|
+
<div class="hrl-page__head">
|
|
3
|
+
<h1 class="hrl-page__title">Payroll runs</h1>
|
|
4
|
+
<div class="hrl-page__actions">
|
|
5
|
+
<a class="hrl-btn hrl-btn--primary" href="<%= hr_lite.new_admin_payroll_run_path %>">New run</a>
|
|
6
|
+
</div>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<section class="hrl-card">
|
|
10
|
+
<% if @runs.any? %>
|
|
11
|
+
<div class="hrl-table-wrap">
|
|
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>
|
|
14
|
+
<tbody>
|
|
15
|
+
<% @runs.each do |run| %>
|
|
16
|
+
<tr>
|
|
17
|
+
<td data-label="Period"><%= run.label %></td>
|
|
18
|
+
<td data-label="Status">
|
|
19
|
+
<span class="hrl-badge <%= { "published" => "hrl-badge--ok", "finalized" => "hrl-badge--warn", "draft" => "hrl-badge--muted" }[run.status] %>"><%= run.status.humanize %></span>
|
|
20
|
+
</td>
|
|
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>
|
|
23
|
+
<td data-label=""><a class="hrl-btn" href="<%= hr_lite.admin_payroll_run_path(run) %>">Open</a></td>
|
|
24
|
+
</tr>
|
|
25
|
+
<% end %>
|
|
26
|
+
</tbody>
|
|
27
|
+
</table>
|
|
28
|
+
</div>
|
|
29
|
+
<%= hrl_pagination %>
|
|
30
|
+
<% else %>
|
|
31
|
+
<p class="hrl-card--empty">No payroll runs yet.</p>
|
|
32
|
+
<% end %>
|
|
33
|
+
</section>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<% content_for(:page_title) { "New payroll run" } %>
|
|
2
|
+
<div class="hrl-page__head"><h1 class="hrl-page__title">New payroll run</h1></div>
|
|
3
|
+
|
|
4
|
+
<section class="hrl-card">
|
|
5
|
+
<%= form_with model: @run, url: hr_lite.admin_payroll_runs_path, local: true do |f| %>
|
|
6
|
+
<%= render "hr_lite/shared/form_errors", record: @run %>
|
|
7
|
+
<div class="hrl-field">
|
|
8
|
+
<%= f.label :period_month, "Month" %>
|
|
9
|
+
<%= f.month_field :period_month, value: @run.period_month&.strftime("%Y-%m") %>
|
|
10
|
+
</div>
|
|
11
|
+
<div class="hrl-field">
|
|
12
|
+
<%= f.label :notes, "Notes (optional)" %>
|
|
13
|
+
<%= f.text_area :notes %>
|
|
14
|
+
</div>
|
|
15
|
+
<div class="hrl-form-actions">
|
|
16
|
+
<%= f.submit "Create run", class: "hrl-btn hrl-btn--primary" %>
|
|
17
|
+
<a class="hrl-btn" href="<%= hr_lite.admin_payroll_runs_path %>">Cancel</a>
|
|
18
|
+
</div>
|
|
19
|
+
<% end %>
|
|
20
|
+
</section>
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<% content_for(:page_title) { "Payroll #{@run.label}" } %>
|
|
2
|
+
<div class="hrl-page__head">
|
|
3
|
+
<h1 class="hrl-page__title">Payroll — <%= @run.label %></h1>
|
|
4
|
+
<div class="hrl-page__actions">
|
|
5
|
+
<a class="hrl-btn" href="<%= hr_lite.admin_payroll_runs_path %>">All runs</a>
|
|
6
|
+
</div>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<div class="hrl-kpis">
|
|
10
|
+
<div class="hrl-kpi"><div class="hrl-kpi__value"><%= @slips.size %></div><div class="hrl-kpi__label">Slips</div></div>
|
|
11
|
+
<div class="hrl-kpi"><div class="hrl-kpi__value"><%= hrl_money(@run.total_gross) %></div><div class="hrl-kpi__label">Gross</div></div>
|
|
12
|
+
<div class="hrl-kpi"><div class="hrl-kpi__value"><%= hrl_money(@run.total_net) %></div><div class="hrl-kpi__label">Net payout</div></div>
|
|
13
|
+
<div class="hrl-kpi"><div class="hrl-kpi__value"><%= hrl_money(@run.total_employer_cost) %></div><div class="hrl-kpi__label">Employer cost</div></div>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<% if @run.warnings.any? %>
|
|
17
|
+
<div class="hrl-flash hrl-flash--warn">
|
|
18
|
+
<strong>Warnings:</strong>
|
|
19
|
+
<ul><% @run.warnings.each do |warning| %><li><%= warning %></li><% end %></ul>
|
|
20
|
+
</div>
|
|
21
|
+
<% end %>
|
|
22
|
+
|
|
23
|
+
<section class="hrl-card">
|
|
24
|
+
<div class="hrl-row">
|
|
25
|
+
<span class="hrl-badge"><%= @run.status.humanize %></span>
|
|
26
|
+
<% if @run.draft? || @run.review? %>
|
|
27
|
+
<%= button_to @run.draft? ? "Compute" : "Recompute", hr_lite.compute_admin_payroll_run_path(@run),
|
|
28
|
+
method: :post, class: "hrl-btn hrl-btn--primary" %>
|
|
29
|
+
<% end %>
|
|
30
|
+
<% if @run.review? %>
|
|
31
|
+
<%= button_to "Finalize", hr_lite.finalize_admin_payroll_run_path(@run), method: :post, class: "hrl-btn" %>
|
|
32
|
+
<% end %>
|
|
33
|
+
<% if @run.finalized? %>
|
|
34
|
+
<%= button_to "Unlock", hr_lite.unlock_admin_payroll_run_path(@run), method: :post, class: "hrl-btn" %>
|
|
35
|
+
<%= button_to "Publish to employees", hr_lite.publish_admin_payroll_run_path(@run), method: :post,
|
|
36
|
+
class: "hrl-btn hrl-btn--primary",
|
|
37
|
+
form: { data: { turbo_confirm: "Publish #{@run.label}? Employees will be notified." } } %>
|
|
38
|
+
<% end %>
|
|
39
|
+
<% if @slips.any? %>
|
|
40
|
+
<a class="hrl-btn" href="<%= hr_lite.register_admin_payroll_run_path(@run, format: :csv) %>">Register CSV</a>
|
|
41
|
+
<% end %>
|
|
42
|
+
<% if @run.draft? %>
|
|
43
|
+
<%= button_to "Delete draft", hr_lite.admin_payroll_run_path(@run), method: :delete,
|
|
44
|
+
class: "hrl-btn hrl-btn--danger",
|
|
45
|
+
form: { data: { turbo_confirm: "Delete this draft run?" } } %>
|
|
46
|
+
<% end %>
|
|
47
|
+
</div>
|
|
48
|
+
</section>
|
|
49
|
+
|
|
50
|
+
<section class="hrl-card">
|
|
51
|
+
<% if @slips.any? %>
|
|
52
|
+
<div class="hrl-table-wrap">
|
|
53
|
+
<table class="hrl-table hrl-table--stack">
|
|
54
|
+
<thead><tr><th>Employee</th><th>Days</th><th>LOP</th><th>Gross</th><th>Deductions</th><th>Net</th><th></th></tr></thead>
|
|
55
|
+
<tbody>
|
|
56
|
+
<% @slips.each do |slip| %>
|
|
57
|
+
<tr>
|
|
58
|
+
<td data-label="Employee"><%= hr_display_name(slip.user) %></td>
|
|
59
|
+
<td data-label="Days" class="hrl-num"><%= slip.payable_days %></td>
|
|
60
|
+
<td data-label="LOP" class="hrl-num">
|
|
61
|
+
<%= slip.effective_lop_days %>
|
|
62
|
+
<% if slip.lop_override %><span class="hrl-badge hrl-badge--warn">override</span><% end %>
|
|
63
|
+
</td>
|
|
64
|
+
<td data-label="Gross" class="hrl-num"><%= hrl_money(slip.gross_earnings) %></td>
|
|
65
|
+
<td data-label="Deductions" class="hrl-num"><%= hrl_money(slip.total_deductions) %></td>
|
|
66
|
+
<td data-label="Net" class="hrl-num"><strong><%= hrl_money(slip.net_pay) %></strong></td>
|
|
67
|
+
<td data-label=""><a class="hrl-small" href="<%= hr_lite.admin_salary_slip_path(slip) %>">Detail</a></td>
|
|
68
|
+
</tr>
|
|
69
|
+
<% end %>
|
|
70
|
+
</tbody>
|
|
71
|
+
</table>
|
|
72
|
+
</div>
|
|
73
|
+
<% else %>
|
|
74
|
+
<p class="hrl-card--empty">No slips yet — compute the run.</p>
|
|
75
|
+
<% end %>
|
|
76
|
+
</section>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<% content_for(:page_title) { "Slip review" } %>
|
|
2
|
+
<div class="hrl-page__head">
|
|
3
|
+
<h1 class="hrl-page__title"><%= hr_display_name(@slip.user) %> — <%= @slip.period_month.strftime("%B %Y") %></h1>
|
|
4
|
+
<div class="hrl-page__actions">
|
|
5
|
+
<a class="hrl-btn" href="<%= hr_lite.admin_payroll_run_path(@slip.payroll_run) %>">Back to run</a>
|
|
6
|
+
</div>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<%= render "hr_lite/salary_slips/slip_detail", slip: @slip, profile: @profile %>
|
|
10
|
+
|
|
11
|
+
<section class="hrl-card">
|
|
12
|
+
<h2 class="hrl-card__title">Tax working</h2>
|
|
13
|
+
<dl class="hrl-deflist">
|
|
14
|
+
<% @slip.tax_details_hash.each do |key, value| %>
|
|
15
|
+
<dt><%= key.humanize %></dt><dd><%= value %></dd>
|
|
16
|
+
<% end %>
|
|
17
|
+
</dl>
|
|
18
|
+
<% if @slip.tax_details_hash["high_income_review"] == "true" %>
|
|
19
|
+
<p class="hrl-flash hrl-flash--warn">Projected taxable income is above ₹50L — surcharge is NOT modelled.
|
|
20
|
+
Set a TDS override after checking with the CA.</p>
|
|
21
|
+
<% end %>
|
|
22
|
+
</section>
|
|
23
|
+
|
|
24
|
+
<% if @slip.payroll_run.review? %>
|
|
25
|
+
<section class="hrl-card">
|
|
26
|
+
<h2 class="hrl-card__title">Overrides</h2>
|
|
27
|
+
<p class="hrl-small hrl-muted">Overrides survive recomputes. Clear a field to remove its override.</p>
|
|
28
|
+
<%= form_with url: hr_lite.admin_salary_slip_path(@slip), method: :patch, scope: :salary_slip, local: true do |f| %>
|
|
29
|
+
<div class="hrl-field">
|
|
30
|
+
<%= f.label :lop_override, "LOP days override (attendance says #{@slip.lop_days})" %>
|
|
31
|
+
<%= f.number_field :lop_override, step: 0.5, value: @slip.lop_override %>
|
|
32
|
+
</div>
|
|
33
|
+
<div class="hrl-field">
|
|
34
|
+
<%= f.label :tds_override, "Monthly TDS override" %>
|
|
35
|
+
<%= f.number_field :tds_override, step: 1, value: @slip.tds_override %>
|
|
36
|
+
</div>
|
|
37
|
+
<div class="hrl-form-actions">
|
|
38
|
+
<%= f.submit "Recompute with overrides", class: "hrl-btn hrl-btn--primary" %>
|
|
39
|
+
</div>
|
|
40
|
+
<% end %>
|
|
41
|
+
</section>
|
|
42
|
+
<% end %>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<section class="hrl-card">
|
|
2
|
+
<%= form_with model: structure, url: url, method: method, scope: :salary_structure, local: true do |f| %>
|
|
3
|
+
<%= render "hr_lite/shared/form_errors", record: structure %>
|
|
4
|
+
<div class="hrl-field">
|
|
5
|
+
<%= f.label :effective_from, "Effective from (1st of month)" %>
|
|
6
|
+
<%= f.date_field :effective_from %>
|
|
7
|
+
</div>
|
|
8
|
+
<div class="hrl-field"><%= f.label :basic, "Basic (monthly)" %><%= f.number_field :basic, step: 0.01, value: structure.basic&.to_s("F") %></div>
|
|
9
|
+
<div class="hrl-field"><%= f.label :hra, "HRA" %><%= f.number_field :hra, step: 0.01, value: structure.hra&.to_s("F") %></div>
|
|
10
|
+
<div class="hrl-field"><%= f.label :special_allowance %><%= f.number_field :special_allowance, step: 0.01, value: structure.special_allowance&.to_s("F") %></div>
|
|
11
|
+
<div class="hrl-field"><%= f.label :other_earnings %><%= f.number_field :other_earnings, step: 0.01, value: structure.other_earnings&.to_s("F") %></div>
|
|
12
|
+
<div class="hrl-field hrl-field--check"><%= f.check_box :pf_applicable %><%= f.label :pf_applicable, "PF applicable" %></div>
|
|
13
|
+
<div class="hrl-field hrl-field--check"><%= f.check_box :pf_on_full_basic %><%= f.label :pf_on_full_basic, "PF on full basic (no ₹15k ceiling)" %></div>
|
|
14
|
+
<div class="hrl-field hrl-field--check"><%= f.check_box :esi_applicable %><%= f.label :esi_applicable, "ESI applicable (auto-exempt above gross ceiling)" %></div>
|
|
15
|
+
<div class="hrl-field">
|
|
16
|
+
<%= f.label :pt_state, "Professional tax state" %>
|
|
17
|
+
<%= f.select :pt_state, HrLite::StatutoryRateCard.for(Date.current)[:pt].keys.map { |s| [ s.humanize, s ] } %>
|
|
18
|
+
</div>
|
|
19
|
+
<div class="hrl-field"><%= f.label :notes %><%= f.text_area :notes %></div>
|
|
20
|
+
<div class="hrl-form-actions">
|
|
21
|
+
<%= f.submit "Save structure", class: "hrl-btn hrl-btn--primary" %>
|
|
22
|
+
<a class="hrl-btn" href="<%= hr_lite.admin_employee_path(@profile) %>">Cancel</a>
|
|
23
|
+
</div>
|
|
24
|
+
<% end %>
|
|
25
|
+
</section>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<% content_for(:page_title) { "Edit salary structure" } %>
|
|
2
|
+
<div class="hrl-page__head">
|
|
3
|
+
<h1 class="hrl-page__title">Edit structure — <%= hr_display_name(@profile.user) %></h1>
|
|
4
|
+
</div>
|
|
5
|
+
<%= render "form", structure: @structure,
|
|
6
|
+
url: hr_lite.admin_employee_salary_structure_path(@profile, @structure), method: :patch %>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<% content_for(:page_title) { "New salary structure" } %>
|
|
2
|
+
<div class="hrl-page__head">
|
|
3
|
+
<h1 class="hrl-page__title">New structure — <%= hr_display_name(@profile.user) %></h1>
|
|
4
|
+
</div>
|
|
5
|
+
<%= render "form", structure: @structure,
|
|
6
|
+
url: hr_lite.admin_employee_salary_structures_path(@profile), method: :post %>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<% content_for(:page_title) { "HR settings" } %>
|
|
2
|
+
<div class="hrl-page__head">
|
|
3
|
+
<h1 class="hrl-page__title">HR settings</h1>
|
|
4
|
+
</div>
|
|
5
|
+
|
|
6
|
+
<section class="hrl-card">
|
|
7
|
+
<%= form_with model: @setting, url: hr_lite.admin_setting_path, method: :patch, local: true do |f| %>
|
|
8
|
+
<%= render "hr_lite/shared/form_errors", record: @setting %>
|
|
9
|
+
<div class="hrl-field">
|
|
10
|
+
<%= f.label :weekend_policy, "Weekly off policy" %>
|
|
11
|
+
<%= f.select :weekend_policy, HrLite::Setting::WEEKEND_POLICIES.map { |p| [ p.humanize, p ] } %>
|
|
12
|
+
<p class="hrl-hint">Drives working-day math everywhere: leave counting, attendance, payroll.</p>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="hrl-form-actions">
|
|
15
|
+
<%= f.submit "Save", class: "hrl-btn hrl-btn--primary" %>
|
|
16
|
+
</div>
|
|
17
|
+
<% end %>
|
|
18
|
+
</section>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<% content_for(:page_title) { "Appraisals" } %>
|
|
2
|
+
<div class="hrl-page__head">
|
|
3
|
+
<h1 class="hrl-page__title">My appraisals</h1>
|
|
4
|
+
<div class="hrl-page__actions"><a class="hrl-btn" href="<%= hr_lite.career_path %>">Career timeline</a></div>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<section class="hrl-card">
|
|
8
|
+
<% if @appraisals.any? %>
|
|
9
|
+
<% @appraisals.each do |appraisal| %>
|
|
10
|
+
<div class="hrl-feed__item">
|
|
11
|
+
<div class="hrl-feed__body">
|
|
12
|
+
<strong><%= appraisal.period_label %></strong>
|
|
13
|
+
<% if appraisal.rating %><span class="hrl-badge hrl-badge--ok"><%= appraisal.rating %>/5</span><% end %>
|
|
14
|
+
</div>
|
|
15
|
+
<a class="hrl-btn" href="<%= hr_lite.appraisal_path(appraisal) %>">Read</a>
|
|
16
|
+
</div>
|
|
17
|
+
<% end %>
|
|
18
|
+
<%= hrl_pagination %>
|
|
19
|
+
<% else %>
|
|
20
|
+
<p class="hrl-card--empty">No shared appraisals yet.</p>
|
|
21
|
+
<% end %>
|
|
22
|
+
</section>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<% content_for(:page_title) { "Appraisal" } %>
|
|
2
|
+
<div class="hrl-page__head">
|
|
3
|
+
<h1 class="hrl-page__title">Appraisal — <%= @appraisal.period_label %></h1>
|
|
4
|
+
<div class="hrl-page__actions"><a class="hrl-btn" href="<%= hr_lite.appraisals_path %>">Back</a></div>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<section class="hrl-card">
|
|
8
|
+
<dl class="hrl-deflist">
|
|
9
|
+
<% if @appraisal.rating %><dt>Rating</dt><dd><%= @appraisal.rating %>/5</dd><% end %>
|
|
10
|
+
<dt>Reviewer</dt><dd><%= hr_display_name(@appraisal.reviewer) %></dd>
|
|
11
|
+
<dt>Shared</dt><dd><%= @appraisal.shared_at.strftime("%d %b %Y") %></dd>
|
|
12
|
+
<% if @appraisal.outcome != "none" %>
|
|
13
|
+
<dt>Outcome</dt>
|
|
14
|
+
<dd><%= @appraisal.outcome.humanize %><% if @appraisal.new_designation %> — <%= @appraisal.new_designation %><% end %>
|
|
15
|
+
<% if @appraisal.effective_date %> (effective <%= @appraisal.effective_date.strftime("%d %b %Y") %>)<% end %></dd>
|
|
16
|
+
<% end %>
|
|
17
|
+
</dl>
|
|
18
|
+
</section>
|
|
19
|
+
|
|
20
|
+
<% if @appraisal.strengths.present? %>
|
|
21
|
+
<section class="hrl-card">
|
|
22
|
+
<h2 class="hrl-card__title">Strengths</h2>
|
|
23
|
+
<p><%= simple_format(@appraisal.strengths) %></p>
|
|
24
|
+
</section>
|
|
25
|
+
<% end %>
|
|
26
|
+
|
|
27
|
+
<% if @appraisal.improvements.present? %>
|
|
28
|
+
<section class="hrl-card">
|
|
29
|
+
<h2 class="hrl-card__title">Areas to grow</h2>
|
|
30
|
+
<p><%= simple_format(@appraisal.improvements) %></p>
|
|
31
|
+
</section>
|
|
32
|
+
<% end %>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<%# locals: month (Date, any day in month), day_status (HrLite::DayStatus), link_builder (optional lambda date -> url) %>
|
|
2
|
+
<% first = month.beginning_of_month
|
|
3
|
+
last = month.end_of_month
|
|
4
|
+
link_builder = local_assigns[:link_builder] %>
|
|
5
|
+
<div class="hrl-mgrid" role="grid" aria-label="Attendance for <%= month.strftime('%B %Y') %>">
|
|
6
|
+
<% %w[Mo Tu We Th Fr Sa Su].each do |d| %>
|
|
7
|
+
<div class="hrl-mgrid__head"><%= d %></div>
|
|
8
|
+
<% end %>
|
|
9
|
+
<% (first.cwday - 1).times do %>
|
|
10
|
+
<div></div>
|
|
11
|
+
<% end %>
|
|
12
|
+
<% (first..last).each do |date| %>
|
|
13
|
+
<% day = day_status.for(date)
|
|
14
|
+
classes = {
|
|
15
|
+
present: "hrl-mgrid__day--present", half_day: "hrl-mgrid__day--half",
|
|
16
|
+
leave: "hrl-mgrid__day--leave", half_day_leave: "hrl-mgrid__day--leave",
|
|
17
|
+
holiday: "hrl-mgrid__day--holiday", weekend: "hrl-mgrid__day--weekend",
|
|
18
|
+
absent: "hrl-mgrid__day--absent", upcoming: "hrl-mgrid__day--muted"
|
|
19
|
+
}[day.kind] %>
|
|
20
|
+
<div class="hrl-mgrid__day <%= classes %>" role="gridcell">
|
|
21
|
+
<% if day.record&.flagged? %><span class="hrl-mgrid__flag" title="<%= day.record.flag_note %>"></span><% end %>
|
|
22
|
+
<span class="hrl-mgrid__num">
|
|
23
|
+
<% if link_builder %>
|
|
24
|
+
<a href="<%= link_builder.call(date) %>"><%= date.day %></a>
|
|
25
|
+
<% else %>
|
|
26
|
+
<%= date.day %>
|
|
27
|
+
<% end %>
|
|
28
|
+
</span>
|
|
29
|
+
<% if day.record&.check_in_at %>
|
|
30
|
+
<span class="hrl-mgrid__tag"><%= day.record.check_in_at.strftime("%H:%M") %><%= day.record.check_out_at ? "–#{day.record.check_out_at.strftime('%H:%M')}" : "" %></span>
|
|
31
|
+
<% elsif day.kind == :absent %>
|
|
32
|
+
<span class="hrl-mgrid__tag">absent</span>
|
|
33
|
+
<% end %>
|
|
34
|
+
</div>
|
|
35
|
+
<% end %>
|
|
36
|
+
</div>
|
|
37
|
+
<div class="hrl-legend hrl-small">
|
|
38
|
+
<span class="hrl-badge hrl-badge--ok">Present</span>
|
|
39
|
+
<span class="hrl-badge hrl-badge--bad">Absent</span>
|
|
40
|
+
<span class="hrl-badge hrl-badge--muted">Upcoming</span>
|
|
41
|
+
</div>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<% record = HrLite::AttendanceRecord.find_by(user_id: hr_current_user.id, date: Date.current) %>
|
|
2
|
+
<section class="hrl-card">
|
|
3
|
+
<h2 class="hrl-card__title">Today, <%= Date.current.strftime("%d %b") %></h2>
|
|
4
|
+
|
|
5
|
+
<div class="hrl-row hrl-small" style="margin-bottom:.6rem;">
|
|
6
|
+
<% if record&.check_in_at %>
|
|
7
|
+
<span class="hrl-badge hrl-badge--ok">In <%= record.check_in_at.strftime("%H:%M") %></span>
|
|
8
|
+
<% end %>
|
|
9
|
+
<% if record&.check_out_at %>
|
|
10
|
+
<span class="hrl-badge hrl-badge--muted">Out <%= record.check_out_at.strftime("%H:%M") %></span>
|
|
11
|
+
<% end %>
|
|
12
|
+
<% if record&.flagged? %>
|
|
13
|
+
<span class="hrl-badge hrl-badge--bad" title="<%= record.flag_note %>">Flagged</span>
|
|
14
|
+
<% end %>
|
|
15
|
+
<% if record.nil? %>
|
|
16
|
+
<span class="hrl-muted">Not checked in yet.</span>
|
|
17
|
+
<% end %>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<% if record&.check_in_at.blank? %>
|
|
21
|
+
<%= form_with url: hr_lite.check_in_attendance_path, method: :post, local: true,
|
|
22
|
+
html: { data: { "hrl-geo-punch": true } } do %>
|
|
23
|
+
<input type="hidden" name="lat"><input type="hidden" name="lng">
|
|
24
|
+
<input type="hidden" name="accuracy_m"><input type="hidden" name="geo_status">
|
|
25
|
+
<button type="submit" class="hrl-btn hrl-btn--primary hrl-btn--big hrl-btn--block">Check in</button>
|
|
26
|
+
<p class="hrl-small hrl-muted" data-hrl-geo-status hidden></p>
|
|
27
|
+
<% end %>
|
|
28
|
+
<% else %>
|
|
29
|
+
<%= form_with url: hr_lite.check_out_attendance_path, method: :post, local: true,
|
|
30
|
+
html: { data: { "hrl-geo-punch": true } } do %>
|
|
31
|
+
<input type="hidden" name="lat"><input type="hidden" name="lng">
|
|
32
|
+
<input type="hidden" name="accuracy_m"><input type="hidden" name="geo_status">
|
|
33
|
+
<button type="submit" class="hrl-btn hrl-btn--big hrl-btn--block">
|
|
34
|
+
<%= record.check_out_at ? "Update check-out" : "Check out" %>
|
|
35
|
+
</button>
|
|
36
|
+
<p class="hrl-small hrl-muted" data-hrl-geo-status hidden></p>
|
|
37
|
+
<% end %>
|
|
38
|
+
<% end %>
|
|
39
|
+
|
|
40
|
+
<a class="hrl-small" href="<%= hr_lite.attendance_path %>">View my month</a>
|
|
41
|
+
</section>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<% content_for(:page_title) { "Attendance" } %>
|
|
2
|
+
<div class="hrl-page__head">
|
|
3
|
+
<h1 class="hrl-page__title">My attendance</h1>
|
|
4
|
+
</div>
|
|
5
|
+
|
|
6
|
+
<%= render "hr_lite/attendance/punch_card" %>
|
|
7
|
+
|
|
8
|
+
<section class="hrl-card">
|
|
9
|
+
<div class="hrl-row" style="margin-bottom:.6rem;">
|
|
10
|
+
<a class="hrl-btn" href="<%= hr_lite.attendance_path(month: (@month - 1.month).strftime("%Y-%m")) %>">←</a>
|
|
11
|
+
<strong><%= @month.strftime("%B %Y") %></strong>
|
|
12
|
+
<a class="hrl-btn" href="<%= hr_lite.attendance_path(month: (@month + 1.month).strftime("%Y-%m")) %>">→</a>
|
|
13
|
+
<span class="hrl-right hrl-small hrl-muted">
|
|
14
|
+
Present <%= @counts[:present] %> · Half <%= @counts[:half_day] %> · Absent <%= @counts[:absent] %>
|
|
15
|
+
</span>
|
|
16
|
+
</div>
|
|
17
|
+
<%= render "hr_lite/attendance/month_grid", month: @month, day_status: @day_status %>
|
|
18
|
+
</section>
|
|
19
|
+
|
|
20
|
+
<% content_for :scripts do %>
|
|
21
|
+
<%= javascript_include_tag "hr_lite/geo_punch", defer: true %>
|
|
22
|
+
<% end %>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<% content_for(:page_title) { "Calendar" } %>
|
|
2
|
+
<div class="hrl-page__head">
|
|
3
|
+
<h1 class="hrl-page__title">Company calendar</h1>
|
|
4
|
+
</div>
|
|
5
|
+
|
|
6
|
+
<section class="hrl-card">
|
|
7
|
+
<div class="hrl-row" style="margin-bottom:.6rem;">
|
|
8
|
+
<a class="hrl-btn" href="<%= hr_lite.calendar_path(month: (@month - 1.month).strftime("%Y-%m")) %>">←</a>
|
|
9
|
+
<strong><%= @month.strftime("%B %Y") %></strong>
|
|
10
|
+
<a class="hrl-btn" href="<%= hr_lite.calendar_path(month: (@month + 1.month).strftime("%Y-%m")) %>">→</a>
|
|
11
|
+
<a class="hrl-btn hrl-right" href="<%= hr_lite.holidays_path %>">Holiday list</a>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<% first = @month.beginning_of_month %>
|
|
15
|
+
<div class="hrl-mgrid">
|
|
16
|
+
<% %w[Mo Tu We Th Fr Sa Su].each do |d| %><div class="hrl-mgrid__head"><%= d %></div><% end %>
|
|
17
|
+
<% (first.cwday - 1).times do %><div></div><% end %>
|
|
18
|
+
<% (first..@month.end_of_month).each do |date| %>
|
|
19
|
+
<% cell_class =
|
|
20
|
+
if @holidays[date] then "hrl-mgrid__day--holiday"
|
|
21
|
+
elsif @calendar.weekend?(date) then "hrl-mgrid__day--weekend"
|
|
22
|
+
elsif @leaves[date].any? then "hrl-mgrid__day--leave"
|
|
23
|
+
end %>
|
|
24
|
+
<div class="hrl-mgrid__day <%= cell_class %>">
|
|
25
|
+
<span class="hrl-mgrid__num"><%= date.day %></span>
|
|
26
|
+
<% Array(@holidays[date]).each do |holiday| %>
|
|
27
|
+
<span class="hrl-mgrid__tag" title="<%= holiday.name %>"><%= holiday.name %></span>
|
|
28
|
+
<% end %>
|
|
29
|
+
<% @leaves[date].first(2).each do |request| %>
|
|
30
|
+
<span class="hrl-mgrid__tag" style="color: <%= request.leave_type.color %>;"
|
|
31
|
+
title="<%= hr_display_name(request.user) %> — <%= request.leave_type.name %>">
|
|
32
|
+
<%= hr_display_name(request.user).split.first %> · <%= request.leave_type.code %>
|
|
33
|
+
</span>
|
|
34
|
+
<% end %>
|
|
35
|
+
<% if @leaves[date].size > 2 %><span class="hrl-mgrid__tag">+<%= @leaves[date].size - 2 %> more</span><% end %>
|
|
36
|
+
</div>
|
|
37
|
+
<% end %>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<div class="hrl-legend hrl-small">
|
|
41
|
+
<span class="hrl-badge hrl-badge--warn">Holiday</span>
|
|
42
|
+
<span class="hrl-badge">On leave</span>
|
|
43
|
+
<span class="hrl-badge hrl-badge--muted">Weekend</span>
|
|
44
|
+
</div>
|
|
45
|
+
</section>
|