# Worker Lookup Modal — Implementation Plan > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** Add a "Worker Lookup" modal to the payroll dashboard that shows a comprehensive financial report card for any active worker — payable amount, loans, recent payments, sizing, and notes. **Architecture:** New AJAX endpoint (`worker_lookup_ajax`) returns JSON with all worker data. Modal HTML + JS in the dashboard template dynamically renders the data using safe DOM methods (textContent, createElement). Worker names across all dashboard tabs become clickable links that open the modal. A "Worker Lookup" button in the header lets you search any active worker. **Tech Stack:** Django views (JSON), Bootstrap 5 modal, vanilla JavaScript (matching existing patterns) --- ### Task 1: Add the AJAX backend endpoint **Files:** - Modify: `core/views.py` (add new view after `preview_payslip` at ~line 2155) - Modify: `core/urls.py` (add new URL pattern at ~line 54) **Step 1: Add URL pattern in `core/urls.py`** Add after the `preview_payslip` URL (line 51): ```python # Worker lookup — AJAX report card for a single worker (returns JSON) path('payroll/worker-lookup//', views.worker_lookup_ajax, name='worker_lookup_ajax'), ``` **Step 2: Add `worker_lookup_ajax` view in `core/views.py`** Add after `preview_payslip` function (after line 2155). The view: 1. Checks admin access (`is_admin`) 2. Fetches the Worker object with all model fields (sizing, license, notes) 3. Calculates Amount Payable: - Unpaid WorkLogs x daily_rate (same logic as `preview_payslip` lines 2069-2083) - Net pending adjustments (additive minus deductive) 4. Outstanding Loans: `Loan.filter(worker=worker, active=True).aggregate(Sum('remaining_balance'))` 5. Paid This Month: `PayrollRecord.filter(worker=worker, date__year=now.year, date__month=now.month).aggregate(Sum('amount_paid'))` 6. Loans This Year: `Loan.filter(worker=worker, date__year=now.year).aggregate(Sum('principal_amount'))` 7. Paid This Year: `PayrollRecord.filter(worker=worker, date__year=now.year).aggregate(Sum('amount_paid'))` 8. Last Payslip: `PayrollRecord.filter(worker=worker).order_by('-date').first()` -> date + amount 9. Last Loan Given: `Loan.filter(worker=worker).order_by('-date').first()` -> date + amount + reason 10. Last Loan Repayment: `PayrollAdjustment.filter(worker=worker, type='Loan Repayment', payroll_record__isnull=False).order_by('-date').first()` -> date + amount 11. Last Advance: `PayrollAdjustment.filter(worker=worker, type='Advance Payment', payroll_record__isnull=False).order_by('-date').first()` -> date + amount 12. Active Loans list: `Loan.filter(worker=worker, active=True).order_by('-date')` -> type, principal, balance, date, reason 13. Current Project: most recent WorkLog -> project name + count of logs on that project 14. Team: `get_worker_active_team(worker)` -> team name **Step 3: Verify imports** Ensure `Sum` is imported from `django.db.models` at the top of `views.py`. Check for existing `from django.db.models import ...` line and add `Sum` if missing. --- ### Task 2: Add the modal HTML to the dashboard template **Files:** - Modify: `core/templates/core/payroll_dashboard.html` **Step 1: Add "Worker Lookup" button in the page header (line 15)** Add a new button in the header button group, before Batch Pay. **Step 2: Add the Worker Lookup modal HTML** Add after the `previewPayslipModal` (after line ~783). The modal contains: - Worker dropdown in header - A body div (`#workerLookupBody`) that gets populated by JS - Placeholder text as default content **Step 3: Pass `active_workers_list` from the view** In `core/views.py`, in the `payroll_dashboard` view, add to the context dict: ```python 'active_workers_list': Worker.objects.filter(active=True).order_by('name'), ``` --- ### Task 3: Make worker names clickable across all tabs **Files:** - Modify: `core/templates/core/payroll_dashboard.html` Replace `worker.name` with clickable links using class `worker-lookup-link` and `data-worker-id` at: - **Pending Payments tab** (line 268) - **Payment History tab** (line 365) - **Loans & Advances tab** (line 432) --- ### Task 4: Add JavaScript to fetch data and render the modal **Files:** - Modify: `core/templates/core/payroll_dashboard.html` (JS section at bottom) **Step 1: Add the `loadWorkerLookup(workerId)` function** This function: 1. Shows a loading spinner in `#workerLookupBody` 2. Fetches `/payroll/worker-lookup//` via fetch API 3. Builds the report card using safe DOM methods (createElement, textContent — no innerHTML with user data) 4. Renders sections: Identity, Quick Stats (4 cards), Recent Activity, Active Loans table, Paid This Year, Sizing & Info Format currency as `R X,XXX.XX` using `toLocaleString('en-ZA', {minimumFractionDigits: 2})`. **Step 2: Add event listeners** - Worker Lookup button click -> open modal with empty dropdown - Dropdown change -> call `loadWorkerLookup(selectedId)` - `.worker-lookup-link` click -> set dropdown value, load data, open modal --- ### Task 5: Update CLAUDE.md **Files:** - Modify: `CLAUDE.md` - Add Worker Lookup documentation to Development Workflow section - Add URL route to the URL Routes table - Update view count from "27 functions" to "28 functions" --- ### Task 6: Test locally 1. Start dev server: `run_dev.bat` 2. Go to `/payroll/` -> verify "Worker Lookup" button appears in header 3. Click "Worker Lookup" -> modal opens with dropdown -> select a worker -> report card loads 4. Click a worker name in Pending Payments -> modal opens with that worker's data 5. Click a worker name in Payment History -> same 6. Click a worker name in Loans & Advances -> same 7. Switch workers via dropdown while modal is open -> data refreshes 8. Verify all sections render correctly: - Quick stats show correct amounts - Recent activity shows dates and amounts (or "None") - Active loans table shows if worker has loans - Sizing and notes display at bottom 9. Verify existing functionality is unbroken: - Preview payslip modal still works - Quick adjust button still works - Pay/Batch pay still works