feat: Pay Salary quick action on home dashboard (deep-link to modal)

Admin Quick Actions tile -> /payroll/?action=pay-salary; the payroll
page auto-clicks the existing paySalaryBtn then strips the param.
Reuses all existing Pay-Salary machinery; param inert server-side.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Konrad du Plessis 2026-05-16 22:58:10 +02:00
parent 56c10ab938
commit 9ab0c68243
3 changed files with 54 additions and 0 deletions

View File

@ -209,6 +209,13 @@
<i class="fas fa-money-check-alt"></i>
<span>Run Payroll</span>
</a>
{# === PAY SALARY — quick path: opens the Pay-Salary modal on /payroll/ === #}
{# Same fa-user-tie icon as the payroll dashboard's Pay Salary button so #}
{# users have one mental model for "salary = fa-user-tie". #}
<a href="{% url 'payroll_dashboard' %}?action=pay-salary" class="quick-action">
<i class="fas fa-user-tie"></i>
<span>Pay Salary</span>
</a>
<a href="{% url 'work_history' %}" class="quick-action">
<i class="fas fa-history"></i>
<span>View History</span>

View File

@ -2116,6 +2116,22 @@ document.addEventListener('DOMContentLoaded', function() {
});
}
// === Quick-action deep-link: /payroll/?action=pay-salary ===
// The home dashboard "Pay Salary" Quick Action links here with this
// param. Auto-click the existing Pay Salary button (which does the
// clean-slate + type=Salary + managers-only scoping + modal open),
// then strip the param so a manual refresh or Back doesn't re-pop
// the modal. Best-effort — never let a deep-link quirk block the page.
try {
var _qsAction = new URLSearchParams(window.location.search).get('action');
if (_qsAction === 'pay-salary' && paySalaryBtn) {
paySalaryBtn.click();
var _u = new URL(window.location.href);
_u.searchParams.delete('action');
window.history.replaceState({}, '', _u.pathname + _u.search + _u.hash);
}
} catch (e) { /* deep-link is best-effort; never block the page */ }
// When the modal is opened from the HEADER button (not quick-adjust,
// not Pay-Salary), clear any pre-selected workers/project and reset
// the type to Bonus.

View File

@ -3307,6 +3307,37 @@ class WorkerListPayTypeFilterTests(TestCase):
)
class PaySalaryQuickActionTests(TestCase):
"""Home dashboard 'Pay Salary' Quick Action: an admin sees a tile
that deep-links /payroll/?action=pay-salary; the param is inert
server-side (no view change). The auto-open click is client-side
JS, verified by manual checklist (not asserted here)."""
@classmethod
def setUpTestData(cls):
cls.admin = User.objects.create_user(
username='psqa_admin', password='pw',
is_staff=True, is_superuser=True,
)
def setUp(self):
self.client.force_login(self.admin)
def test_home_has_pay_salary_quick_action(self):
resp = self.client.get('/')
self.assertEqual(resp.status_code, 200)
# The tile links to the payroll dashboard with the deep-link param.
self.assertContains(resp, '?action=pay-salary')
# And is labelled "Pay Salary".
self.assertContains(resp, 'Pay Salary')
def test_payroll_dashboard_ignores_action_param(self):
# The param is purely a client-side signal; the view must not
# care about it — same 200 as a plain /payroll/ load.
resp = self.client.get('/payroll/?action=pay-salary')
self.assertEqual(resp.status_code, 200)
class WorkHistoryTeamFilterTests(TestCase):
"""The /history/ page accepts ?team=<id> to narrow to logs tagged
with that team, ?team=none for logs with no team set, and empty