Skip to content

HRM React admin redesign (free) - #1589

Open
arifulhoque7 wants to merge 344 commits into
wp-erp:developfrom
arifulhoque7:feature/hrm-react-admin-redesign
Open

arifulhoque7 wants to merge 344 commits into
wp-erp:developfrom
arifulhoque7:feature/hrm-react-admin-redesign

Conversation

@arifulhoque7

@arifulhoque7 arifulhoque7 commented Jun 12, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Rebuilds the HRM admin as a React SPA (admin.php?page=erp-hr), replacing the legacy Vue/PHP screens with parity coverage across People, Employee profile (v4), Leave, Reports, Org Chart and self-service My Profile.

Highlights in this branch:

  • Employee profile v4 (sidebar layout, field-icon chips); My Profile now renders the same v4 view so icons match the single-employee view.
  • Org Chart redesigned to a top-down team-cluster layout (horizontal person cards, primary-tinted connectors, dashed department clusters, team sidebar, dotted canvas).
  • HR reports pagination — headcount + salary history paginate like the attendance report.
  • Synced with develop (no conflicts).
  • erp_html_form_input() radio fix — options printed their label as a bare text node, so it could not be styled (16.8px against the form's 14px on the public job page) and clicking it did not select the radio. Options are now wrapped in a <label> with a .checkbox-value span, matching the multicheckbox branch beside it; also drops a stray quote that emitted an invalid attribute and a duplicated shared id.

Notes

  • React app builds via pnpm; dist-react is gitignored.
  • Legacy Vue paths retained for modules not yet migrated.

🤖 Generated with Claude Code

QA Handoff: WP ERP v2.0.0 (Free + PRO)

Release QA · feature branch → develop · feature/hrm-react-admin-redesign → develop · diff as of 2026-10-09

Baseline is the last release: WP ERP Free v1.17.10 (wp.org, 2026-10-05) and WP ERP PRO v1.7.0 (2026-08-13). Everything below is on the redesign branch and not in those releases.

This release is one long-lived branch per repo, the HRM React admin redesign, plus a business-flow, architecture and REST v2 review done on 2026-10-06. The branch exists on the author's fork; the PRs are #1589 (free) and wp-erp/erp-pro#612 (pro). Release issue: weDevsOfficial/plugin-internal-tasks#69.

Scope Count
Major features 12
Improvements 15
Fixes (this handoff, 2026-10-06 to 10-09) 42
Repos changed 2 (free + pro), 11 pro React bundles (incl. the HR Frontend portal) + 1 shared package
Linked issues 152 free PR · 301 pro PR (all on wp-erp/erp-pro)
DB changes No new migration files. One self-healing table check (payroll)

Release train: what is actually in scope

Repo Branch ahead of develop Behind Scope
wp-erp/wp-erp (free) 344 commits 1 (merge develop before release) 455 files, +109,527 lines. New React HR app, REST erp/v2, engine switch. HR erp/v1 routes removed from free (now in Pro).
wp-erp/erp-pro (pro) 442 commits 0 1,027 files, +575,009 lines. 10 pro React bundles, pro erp/v2 controllers, @erp/hr-shared, HR Frontend portal rebuilt as its own React app, HR erp/v1 routes (moved from free, hardened like v2).
Legacy Vue / AJAX HR screens unchanged on purpose Still shipped; reachable with "View previous version".
Accounting, CRM not redesigned Touched for safety: accounting /accounting/v1/employees* now needs erp_ac_manager (it returned full HR records to any employee); PeopleController 404/501 guards, TaxRateNames/BankAccounts null guards; CRM contact groups orderby allowlist, contact feed guards. Regression-check the Accounting Employees screens and CRM contact groups.

Major Features: test in depth

1. New HR admin (React) and the engine switch

Repos: wp-erp · erp-pro · PRs: #1589 · erp-pro#612

The whole HR admin is rebuilt as one React app at admin.php?page=erp-hr, with pro modules mounting into it. The legacy Vue admin stays in the plugin.

Gating, read this first: which admin a site sees is decided by HR settings (HR Admin Interface) and the install's age; a user can switch with "View previous version" / "View legacy version". If the React build is missing, the page falls back to legacy and pro React bundles do not load at all. If the bundles were not built for the test zip, the redesign will look absent rather than broken.

How to test

  1. Fresh install: confirm the React admin is the default. Upgrade from 1.17.10 with data: confirm the default follows the setting and nothing is lost.
  2. Switch to legacy and back from the banner and the footer link; the choice must persist per user.
  3. Walk the top bar as HR manager, department lead and employee: each role sees only its modules (employee: Dashboard, People, My Leave, Reimbursement).
  4. Open every module page and reload on a deep link (#/payroll/payruns, #/employees/{id}?tab=leave): the same view must come back.
  5. Phone width (390 px): no sideways page scroll on any page; wide tables scroll inside their own box.
  6. Dark mode toggle and the command palette.

Edge: pro active with an older free (below WPERP_HRM_MIN_VERSION): pro React is skipped with an admin notice, legacy keeps working.

Regression risk: every legacy HR screen (still shipped), the WP admin menu and capabilities for all ERP roles.


2. People: employees, profile, departments, designations, requests

Repos: wp-erp · erp-pro (requests, custom fields)

Employee list (table and grid), create, edit, single profile with tabs (Overview, Job, Leave, Notes, Performance, Permission, plus pro tabs), departments and designations with a tree, org chart, announcements, and People › Requests (leave, remote work, resignation, asset, reimbursement).

How to test

  1. Create an employee with every required field; edit as HR; edit own profile as the employee (HR-only fields must be locked).
  2. Profile tabs as HR, as the reporting manager and as the employee. A reporting manager can add notes and performance reviews for their team.
  3. Notes: add more than 20 notes and use Load more; times must match the site's clock in any browser timezone.
  4. Departments and designations: with more than 100 rows, every row must appear in the table, the tree and the parent picker.
  5. Requests: Remote Work and Resignation lists page through all rows; tab badges match the lists; My Profile "Your Requests" shows only the viewer's own, also for an HR manager.
  6. Terminate, reactivate, print, CSV import with column mapping, CSV export.

Edge: a WP administrator's account cannot be changed by an HR manager. Terminated employees cannot file leave.

Regression risk: employee list filters and counts, role assignment, the legacy employee screens.


3. Leave: requests, policies, entitlements, calendar, financial years

Repos: wp-erp · erp-pro (Advanced Leave)

How to test

  1. Policy → entitlement → request → approve / reject, as HR. Employee files from the dashboard calendar and from My Leave.
  2. Financial years: add a year; try to delete a year that leave records use (must be refused with a clear message).
  3. A request in a past leave year must be refused at creation with "You can not modify past leave year requests".
  4. Counts on the leave requests tabs must match the list for the chosen year.
  5. Calendar: holidays show as whole days; company days off are shaded, not always Saturday and Sunday.
  6. Department lead: with core leave only, the leave requests screen is HR's. Leads approve only with Advanced Leave multi-level approval set up.

Advanced Leave (pro): half-day, accrual, segregation, unpaid leave, multi-level approval, carry forward and encashment.

  1. Set carry forward 5 and encashment 2 on a previous-year policy; open Leave › Forward Leaves; the preview shows available, carry, encash and amounts to two decimals; Apply; the employee's new-year balance grows by the carried days and an encash request is recorded; export the encash CSV.
  2. Edit a policy and save without touching the Advanced Leave fields: carry forward, encashment and accrual must stay. Half-day can be switched on and off.

Regression risk: leave balances, the legacy leave screens, leave emails.


4. Attendance (pro)

Overview, logs, shifts, shift assignment, reports, settings, self check-in from the dashboard.

How to test

  1. Create a shift, assign by employee (search for someone far down the alphabet: the picker must find them), by department, by designation.
  2. Check in and out as an employee; record attendance as HR; CSV import.
  3. Employee report and the profile Attendance tab for a period that contains approved leave and a weekend: both render (they used to crash).
  4. Logs grid: today and future days with no punch show "Not yet", not Absent; the Absent filter does not count them.
  5. Delete a shift: it can no longer be read, edited or assigned.

Regression risk: attendance settings (grace time), the digest and report exports.


5. Payroll (pro)

Pay calendars, pay items and categories, payruns, payslips, reports, settings.

Gating: approving a payrun posts to Accounting. Pay Components needs the pay item category table, which older sites did not have; it is now created automatically on the first admin load after the update.

How to test

  1. Pay calendar for a few employees (search for a late-alphabet name). One calendar per type is the rule.
  2. New payrun wizard → payrun detail → adjustments → Approve → payslip appears for the employee (profile Payslip tab and dashboard).
  3. Adjustments: add two with the same pay item, delete one: only that one goes.
  4. Salary: clear a text field (tax number, bank) and save: it stays cleared. Bulk pay item: set an amount to 0.
  5. Change a payrun's payment date: the payslip month follows.
  6. Send payslips: refused before approval.

Edge: approval queues payslip mail through WP-Cron; "Send payslips" sends again (same as legacy).

Regression risk: accounting vouchers from approval, undo approval, legacy payroll screens.


6. Recruitment (pro), including the public job pages

Job openings with a wizard, question sets, stages, candidates (table and grid), applicant detail, interviews, to-dos, hire, reports, AI job writer and AI CV analysis, plus the public job list and application form.

Gating: AI features need an AI key and a job with AI enabled; without them they look absent.

How to test

  1. Create a job through the wizard; publish; open the public page logged out.
  2. Public application: answer all nine question types, upload a CV, solve the captcha, submit. The candidate appears in the admin with the CV and every answer.
  3. Add a candidate from the admin as an HR manager (not an administrator) with a CV: must succeed.
  4. Search candidates: the total and pager follow the search.
  5. Hire: required fields are named when missing; "Main Location" is accepted; country names read as names; after hiring the new employee profile opens.
  6. AI job writer: saved department, experience and employment type show correctly on the job form.

Regression risk: legacy recruitment admin, application emails, CV downloads.


7. Documents, Training, Assets, Reimbursement (pro)

How to test

  1. Documents: upload, new folder, share with all, by department, by designation, by employee (search a late-alphabet name); the employee sees it under Shared with me and can open it.
  2. Training: create for a department; the employee sees it; HR marks it complete from the employee's Training tab (rating 1 to 10).
  3. Assets: employee requests a catalogue item; HR approves as returnable; the allotment offers Return. Reject works only on pending, undo only on rejected. Editing a variable asset saves its name and category.
  4. Reimbursement: employee files a claim with a receipt (the upload must work); a user with the accounting manager role pays it from Accounting; the employee sees Closed. A user without that role can still read the list, and rows are not links for them.

Regression risk: document sharing rules, asset stock counts, accounting reimbursement transactions.


8. Workflow and Custom Field Builder (pro, standalone pages)

Both are their own admin pages (page=erp-workflow, page=custom-field-builder), not routes inside the HR app.

How to test

  1. Workflow: create a rule (HR, Requested Leave, Send Email to a named employee), Save & Activate; a success toast shows; file a leave; after the next WP-Cron run the email arrives and the run count is 1.
  2. Custom fields (administrator): add a text field to an employee section; it shows on the profile and the edit form and saves.
  3. Both pages load with no console errors when opened directly.

Edge: the workflow [start_date] placeholder prints a raw timestamp (same as legacy, not changed).


9. Dashboard and reports

How to test: dashboard as HR, lead and employee (widgets per role; upcoming holidays sorted, including one in progress); every report page; CSV exports open in a spreadsheet without running formulas.


10. Welcome, motion and hover scrollbars (free shell; pro pages inside it and the Workflow / Custom Fields pages)

Switching to the new design opens a one-time full-screen welcome (still logo, drifting glows, word-by-word headline); the old "new HR experience" notice no longer stacks on it. Then one motion system on every screen: sections cascade, cards stagger, dashboard numbers count up after their card lands, charts draw last, a changed number rings its card once, dialogs and menus open and close with short eased motion, rows fade in, buttons press, touch devices get a 10ms haptic tick, headings rise out of a soft blur. Scrollbars are thin and show only while hovering a scroll area. Same as FlyHR minus the animated logo. (erp-pro#1056, #1057)

How to test

  1. A user who has never switched: switch to the new design, the welcome shows once; reload after closing, it does not return; Esc closes it; the old notice is gone. Switching back to legacy and forward again does not show it again.
  2. After closing, the dashboard plays its entrance in order; change a department count and Refresh, only that card rings.
  3. Leave Entitlements: scroll down, Assign Policy, open both selects, close: the page has not moved (it used to jump to the top). Same on page=erp-workflow and Custom Fields.
  4. Short window, Employees → Export: no scrollbar at rest, a thin one on hover; status tab strips never show one.
  5. macOS Reduce motion: nothing animates; Android: a button tap vibrates briefly, a mouse click never does.

Regression risk: legacy screens untouched (CSS is scoped to #erp-hr-app and plugin-ui overlays).


11. HR Frontend portal (pro): the new HR app outside wp-admin

The employee portal (/<HR Dashboard Slug>/, WP ERP → Settings → HR → HR Frontend) is now its own build of the new HR app (modules/pro/hr-frontend/react), not the previous portal app. Same screens, API and per-role rules as wp-admin; on the portal the sidebar is always used (icon rail that opens over the page on a phone), there is no wp-admin bar, the brand shows the HR Dashboard Logo and Title settings, old path bookmarks (/<slug>/profile, /<slug>/employees/12) open the same screen, and Pro module screens (Attendance, Documents, Training, Recruitment, Assets, Payroll, Reimbursement, Advanced Leave, custom fields) appear as in wp-admin. Only the app's own scripts and styles print: no theme or other plugin runs on the page. (erp-pro#1064)

How to test

  1. As admin, HR manager, employee and a leaver (terminate a test employee): open the portal, walk every sidebar entry. Employee: Dashboard, People (All Employees, Org Chart), My Leave, Reimbursement only; admin-only screens show "No access". Leaver: lands on My Profile, read-only (only Print), menu shows My Leave only.
  2. Employee opens a colleague's profile: Overview only, no hire source, end date, phone, address, birth date, salary or custom field values (check the network tab too).
  3. Phone width (390px): icon rail, no sideways scroll; expand opens the menu over the page; picking an item or tapping outside closes it.
  4. Settings → HR → HR Frontend: change logo, title and slug (wp-admin, wp-json or an existing page's slug are refused through the REST route), enable "Redirect to frontend" and log in as an employee: lands on the portal; an admin still goes to wp-admin.
  5. Logged out: /<slug>/ redirects to login and back; a subscriber is sent to wp-admin.

Regression risk: the portal build must be in the zip (bin/build-react.sh builds hr-frontend); without it the page shows "The HR portal is not available right now".


12. HR v1 API moved to Pro, leavers read-only, no colleague data for employees (free + pro)

The HR erp/v1 REST routes (employees, departments, designations, birthdays, reports, leave entitlements/holidays/policies/requests, announcements, company) moved from WP ERP to Pro (includes/Feature/HRM/Api/V1): same paths and responses, hardened to the v2 rules. Someone who has left keeps reading their own record but changes nothing. An employee no longer receives any colleague's salary, birth date, contact, address, family or custom field values through any route. (erp-pro#1062, #1063, #1065)

How to test

  1. Pro active: the mobile app and /wp-json/erp/v1/hrm/* behave as before for allowed actions; as an employee, PUT /erp/v1/hrm/employees/<self> with pay_rate/department/status leaves them unchanged, a login email change is 403, another employee's record is the limited peer card.
  2. Pro deactivated: /wp-json/erp/v1/hrm/employees is 404 (breaking for free-only integrations, see blocker 10); free's HR screens are unaffected.
  3. As a plain employee: GET /wp-json/erp/v1/accounting/v1/employees is 403 (it returned every employee's HR record and ledger).
  4. Terminate a test employee and log in as them: own profile tabs readable; Edit, Leave, Remote Work, Resign, document upload and New Request (Reimbursement) are gone, and the API refuses them.

10. HR Frontend portal (rebuilt), the employee-facing app outside wp-admin

The portal at the dashboard slug (here /wp-erp-arif/) is now its own build of the new HR app with a fixed sidebar (phone: overlay menu). Legacy screens all exist in the new design; see the 2026-10-09 round below for the per-role checks.

  1. Employee: dashboard (calendar, attendance, payslips, who's out, birthdays, announcements), People directory with filters, own profile with every tab, leave request, reimbursement, announcements page + bell.
  2. HR manager: everything in the admin app (departments, designations, announcements, documents, reports, Pro modules) inside the portal.
  3. Settings (ERP Settings > HR > HR Frontend): slug (reserved slugs such as wp-json refused, old slug stops working after a change), title, logo, login redirect for employees.
  4. Module on/off: HR Frontend off → portal URL 404; on → works without re-saving permalinks. Other Pro modules off → their menus and profile tabs disappear.
  5. Leavers (terminated, resigned, trashed): notice only, no app.

Improvements: verify behaviour

Area Change What to check Ref
free + pro Pro bundles no longer add their own request middleware inside the HR app Force an error on a free screen: the message is the server's, not a generic or pro-labelled one erp-pro#1048
pro Pro React loads only when the free React app is on the page Remove free's build: page falls back to legacy with no console errors from pro erp-pro#1048
pro Workflow and Custom Fields pages take free's script versions from its build files Rebuild free only: both pages still load, no stale script erp-pro#1048
free + pro Page counts use the real page size; pro lists capped at 100 Any list with more than 100 rows pages correctly erp-pro#1048
pro Workflow and recruitment capabilities granted through the role sync Site where the modules were already active: HR manager can open both after the update erp-pro#1048
pro Dates on payslip widget, payroll dashboard, training tab and worktime tooltip use the site format No YYYY-MM-DD on those screens erp-pro#1048
free Times sent with the site's offset A note saved at 11:16 site time reads 11:16 in any browser timezone erp-pro#1047
free Forward Leaves amounts to two decimals No long decimals in the preview or CSV erp-pro#1052
free One-time welcome when a user first switches to the new design (still logo, drifting glows, word-by-word headline); the page waits behind it. The old "new HR experience" notice no longer stacks on top Switch a user to the new design: welcome shows; reload after closing: it does not return; Esc closes it; the old notice is gone erp-pro#1056
free Motion that serves clarity: sections cascade, cards stagger, dashboard numbers count up after their card lands, charts draw last; a changed number counts from the old value and rings its card once; dialogs/sheets/menus/selects open and close with short eased motion; rows fade in; buttons press; 10ms haptic on touch; headings rise out of a soft blur. Off with reduced motion Dashboard after the welcome; change a department count and Refresh (only that card rings); open any dialog and select; macOS Reduce motion: nothing animates erp-pro#1056
free Scrollbars show only on hover, thin, in every scroll area including dialogs, menus and selects (overlays drew the browser default bar) Short window, Employees → Export: no bar at rest, thin bar while hovering the dialog; status tab strips never show one erp-pro#1056

Fixes: confirm resolved + regression

Area Fix What to check Ref
free Accounting employees routes (/accounting/v1/employees, /{id}, /{id}/transactions) gave every employee each employee's full HR record and ledger As an employee: 403; Accounting → People → Employees still works for an accounting manager erp-pro#1062
pro HR v1 employee routes passed a colleague's custom field values (emergency contact etc.) to every employee As an employee, GET /erp/v1/hrm/employees: colleagues' rows carry directory fields only erp-pro#1063
free + pro Leavers kept self-service writes (leave, experience, education, dependents, documents, reimbursement claims) Terminated employee: those writes 403, reads of their own record 200 erp-pro#1062, #1063
free CI uploaded the erp-pro source as a downloadable artifact of the public repo playwright.yml run: no erp-pro-build artifact erp-pro#1062
free PHP 8 fatal errors on missing records in Accounting (people, address, purchases, tax rate names, check email) and CRM (activities, contact groups); CRM contacts printed the raw row and stopped On PHP 8.x open those screens and a deleted record's URL: 404, no critical error erp-pro#1066
free Deprecations on PHP 8.1 to 8.5 ((boolean) cast, ${var}, stripslashes(null), null array key) WP_DEBUG on PHP 8.4: nothing from plugins/wp-erp/ outside vendor/ in debug.log erp-pro#1066
pro IntelliSMS gateway used split() (removed in PHP 7.0): sending was a fatal error on every supported PHP Send a test SMS with IntelliSMS erp-pro#1067
pro Attendance HR entry for an unparsable date was a fatal error; self check-in passed null to strtotime(); employees/{id}/experiences/{id} always 500 Attendance → HR entry with a bad date: 400; check in with no shift; open one experience by id erp-pro#1067
pro PHP 8.x deprecations in leave policies, accounting reports and Deals WP_DEBUG on PHP 8.4: nothing from plugins/wp-erp-pro/ in debug.log erp-pro#1067
pro Reimbursement receipts never uploaded from the React form Attach a PDF and an image to a new claim erp-pro#1045
free View legacy version did nothing on Workflow and Custom Fields (the page stayed on the new UI) On each page click View legacy version: the legacy screen opens and stays; Try the new experience brings the new UI back erp-pro#1055
pro Request rows sent non-accounting users to a "not allowed" page Employee and plain HR manager click a row: nothing happens erp-pro#1045
pro Employee pickers could not find anyone past the first 20 Pay calendar, shift assign, document share, training: search a late name erp-pro#1046
pro Catalogue asset requests always failed; approved allotments could not be returned Feature 7 step 3 erp-pro#1049
pro Attendance report crashed on leave or weekend days Feature 4 step 3 erp-pro#1049
pro Candidate total ignored the search Feature 6 step 4 erp-pro#1049
free + pro Remote Work and Resignation showed only the newest 20 Feature 2 step 5 erp-pro#1049
free + pro My Profile showed HR managers everyone's requests Feature 2 step 5 erp-pro#1049
free Notes tab broke past 100 notes Feature 2 step 3 erp-pro#1049
pro Payroll: adjustment delete, salary field clear, bulk 0, payment date Feature 5 steps 3 to 5 erp-pro#1050
pro Recruitment: AI job meta, hire location, all CVs listed Feature 6 steps 5 and 6; an applicant with two CVs shows both erp-pro#1050
pro Assets: reject / undo state, variable asset edit, unknown ids 404 Feature 7 step 3 erp-pro#1050
pro Attendance: future days Absent, deleted shifts still usable Feature 4 steps 4 and 5 erp-pro#1050
free In-use financial year delete silently ignored Feature 3 step 2 erp-pro#1050
free Departments and designations cut at 100 Feature 2 step 4 erp-pro#1050
free Reporting manager tabs read-only although allowed Feature 2 step 2 erp-pro#1050
pro HR managers could not add a candidate with a CV Feature 6 step 3 erp-pro#1051
pro Payroll category table missing on older sites Pay Components opens on an upgraded site erp-pro#1048
free Past-year leave request stuck pending; policy update reset Advanced Leave settings; half-day could not be turned off Feature 3 steps 3 and 8 erp-pro#1052
free A select inside a dialog scrolled the page underneath to the top and left it there (plugin-ui scrolls the option into view before positioning the popup) Leave Entitlements: scroll down, Assign Policy, open both selects, close: the page has not moved erp-pro#1056
free An empty list scrolled in a 900px window; Export Employee description centred under a left-aligned title Empty filter on any list: no scrollbar; Employees → Export: text left-aligned erp-pro#1056
free Rich-text editors showed an AI Assist button that did nothing Announcements form and pro email editors: no AI Assist button; Bold, links, lists still work erp-pro#1056
free Unpaid Leaves total printed as a raw number (11333.333…) Unpaid Leaves after Calculate: Total shows two decimals, like Forward Leaves erp-pro#1056
free Dashboard stat card looked cut at the top after hover and click (focus ring stayed on the link while the card lifted) HR Dashboard: click Request, go Back, Tab onto cards: ring hugs each lifted card, no gap at the bottom erp-pro#1056
free CSV export printed a PHP 8.4 deprecation (fputcsv() without $escape) On PHP 8.4 with WP_DEBUG_LOG, export employees/CSV: no deprecation in debug.log; file unchanged. Still runs on PHP 7.4 erp-pro#1056
free Employee v2 stored any Reports To id, including no employee and the employee themselves API: reporting_to = self or 999999 → 400 "Reports To must be another employee."; a real manager saves erp-pro#1056
free HR v2 + React: Back left the app after create/edit; Update Job Information opened empty and the header stayed stale; future-dated job changes, 0-day policies, impossible holiday end dates and year-plus ranges accepted; bulk delete of -1 hit row 1; over-long leave reason 500; balance card total counted refunded days Open #/employees/new in a fresh tab and create: lands on People; Job > Update Job Information prefilled, header updates; tomorrow-dated job change, 0-day policy, holiday end 2026-02-30 or over a year → 400; bulk [-1] → 400; approve then reject: card N of N erp-pro#1056
free Dashboard department chart labelled every other bar; leave requests Reason column could shrink to nothing; profile stat label cut off Dashboard with 8+ departments: every bar named; Requests Reason readable at 1366px; profile Leave Available label in full erp-pro#1056
free Trashed employees still counted in the dashboard department bars, the headcount report total and list, salary history and years of service; the Org Chart offered "No Team" for terminated or trashed rows and then showed an empty chart Trash one employee: their department bar, the headcount total and list drop by one, they leave salary history and years of service; restore: they return. Org Chart: "No Team" shows only while an active employee has no department erp-pro#1058

2026-10-09 round: adversarial review, action visibility, legacy portal parity

Users for these checks: employee 234 (Abigail Vance), colleague 214, department lead 219 (dept 2 Product), HR manager 10298 (qa.hr), admin 1, leavers 243 (terminated), 244 (resigned), 245 (trashed), 564 (terminated).

HR Frontend portal (/wp-erp-arif/)

ID Role Steps Expected
PT-01 243 / 244 / 564 Open the portal Notice "Your employment has ended..." with Log out; no app (same as wp-admin, where the HR menu is gone)
PT-02 234 Top bar Announcement bell with unread dot; latest 5; View all opens #/announcements (read-only list, opens the dialog, marks read)
PT-03 234 People Tabs Active, Terminated, Resigned, Deceased, All (no Trash); a former colleague's profile opens
PT-04 234 Open colleague 214 Basic Info (names, ID, email) + type, status, hire date, department, job title only; no contact, location, reporting line, custom fields
PT-05 234 My profile > Leave Casual Leave: Entitled 10, Taken 0, Scheduled 1 (approved 15 Oct), 9 of 10 left
PT-06 234 My profile > Overview Shift shown (General); not shown on a colleague
PT-07 234 User menu Name, designation (Software Engineer), email, My profile, Log out (no WordPress confirm screen)
PT-08 any Header / logo Page title per route; logo goes to the dashboard; no admin-bar assets loaded
PT-09 10298 #/reports Report index cards (no redirect)
PT-10 10298 Profile > Job Pay rates blurred with a per-row show toggle

Action visibility (both apps)

ID Role Where Expected
AV-01 234 / 219 People list + grid No checkboxes, no bulk bar; HR sees them
AV-02 219 Leave requests Team pending row: Approve, Reject, Forward (no Delete); own request: no actions
AV-03 219 Profile of a dept 2 member Performance tab readable (200); other departments 403
AV-04 1 (no employee record) Dashboard / My requests No Take a Leave, Resign, Remote Work
AV-05 234 Own Assets tab Delete Request on own pending request works; not on approved
AV-06 10298 Pro lists (assets, allotments, requests, shifts, trainings, jobs, candidates, AI pool) Checkboxes for HR only; Recruitment AI settings Save/Test only for admins; Workflow CRM rules read-only for HR; builder offers only grantable roles

Server fixes to regress

ID Check Expected
SV-01 Submit the same leave twice at once (double click / two tabs) One request; the other gets "There is already a leave request on these dates."
SV-02 Bulk delete a leave type deleted in another tab Error message, no fatal
SV-03 v1 GET /erp/v1/hrm/leaves/requests as employee Upcoming approved leave listed (was always empty); /leaves/policies returns real names
SV-04 Recruitment Jobs: Open/Expired tabs, search, sort by applicants Same rows and counts as before, faster
SV-05 Payroll salary item amount 1e25 400 (was saved)
SV-06 Attendance with "Trust X-Forwarded-For" Uses the right-most address

Commits: free 72d9e603c, 9cbcd6f69, be3f06125, e8fe9ce81, 58670dd09; Pro 6c21227a, 503dd388, f92ec1cd, e7ca4076, 38a854b0, afa89ce6. PHP lint 7.4/8.0/8.5 clean, PHPCompatibility 0, REST sweep 0 thrown, sensitive-data probe unchanged.

Release blockers and scope notes

  • HR erp/v1 REST moved to Pro. Free registers only erp/v2. The v1 HR routes (used by legacy and mobile clients, not by the new apps) ship with Pro, are gated like v2, and are off when Pro or its licence is inactive (owner decision). A Lite-only test run skips the v1 API specs (@pro).
  • Leavers have no HR access: wp-admin HR menu gone (as before), portal shows a notice. HR still sees and reactivates them.
  • Seed data on wp-erp.test: the licence counts 50 users; most seeded staff carry a placeholder role and cannot log in to HR. Use the users named in the 2026-10-09 round.

Read before scoping the cycle.

1. The React bundles are not in git (now 11 pro bundles, the HR Frontend portal included). assets/dist-react is ignored in both repos. The release build (bin/build.sh, which runs bin/build-react.sh: @erp/hr-shared first, then every module) must run before zipping, and the zip must come from that working tree. Everything changed on 2026-10-06 in React is source only until the next build. Confirm the test zip was built from the current branch head before QA starts, or stale bugs will be reported.

2. Version numbers are not bumped. Headers still say free 1.18.0 and pro 1.7.0; the release is 2.0.0 for both. At release: free header, WPERP_VERSION, package.json; WPERP_HRM_MIN_VERSION in free and pro; pro header and package.json (the zip name comes from package.json).

3. Free and pro ship together, free first. Pro guards every call into free, so an older free keeps legacy working, but the fixes that span both (list page counts, My Profile requests) need both updated.

4. The branch is on a fork. Both PRs target develop and are up to date with it as of 2026-10-06 (0 commits behind). Re-sync before merge if develop moves.

5. Channels. Free: tag → wp.org through CI. Pro: local release zip → support → EDD (no Appsero).

6. Open items, not in this release unless decided:

  • wp-erp/erp-pro#1044: asset item shown by code, "Select Group" placeholder, raw dates and type slug on asset screens.
  • wp-erp/erp-pro#1038: uploaded CVs, HR documents and receipts are served from public upload URLs. Owned by the security pass, not this handoff.
  • List endpoints run extra queries per row (performance only), and the v2 response style is not uniform across pro modules.

7. Background jobs. Workflow actions and payslip emails run through WP-Cron. On a staging site with cron disabled or no traffic they will look broken: trigger cron, or run wp cron event run --due-now.

8. Things that only look broken without setup: AI features (key + job setting), reimbursement pay (accounting manager role), Custom Field Builder (administrator only), lead leave approval (Advanced Leave multi-level).

9. Coverage this cycle did not give. Tested on one local site (single site, English, Asia/Dhaka, 400 employees). Not tested here: multisite, RTL and other locales on today's changes, Dropbox document storage, a real upgrade of a production-sized 1.17.10 database, and the legacy Vue screens after today's shared PHP changes. Budget time for an upgrade test and one pass of the legacy admin.

10. The HR v1 REST API now ships in Pro. On a site with WP ERP but no Pro, /wp-json/erp/v1/hrm/* is gone (custom integrations on it stop working), and a site that updates WP ERP before Pro loses it until Pro is updated. Release both together and say so in the changelog.

11. PHP 7.4 to 8.5. Every PHP file lints on 7.4, 8.0, 8.1, 8.2, 8.3, 8.4 and 8.5, PHPCompatibilityWP (7.4+) is clean, and every /erp/ GET route runs without a fatal error or deprecation from WP ERP code on each. Left: vendor/illuminate/* raises "implicitly nullable" deprecations on 8.4+ (needs a release that drops 7.4). The free PR's old phpcs.yml job (PHP 7.3, 5.6 target, whole-file WPCS) has failed on every push since before this work.


Generated from git log upstream/develop..feature/hrm-react-admin-redesign in wp-erp and erp-pro, the 2026-10-06 flow tests and the 2026-10-09 portal, data-exposure and PHP 7.4 to 8.5 runs; updated 2026-10-09. Issue numbers resolve against wp-erp/erp-pro unless a repo is named.

Related issues

Free HRM React admin redesign. Feature issues (free + pro) are logged on the pro repo wp-erp/erp-pro by convention; this PR closes its own wp-erp issues plus the [FREE] issues tracked on erp-pro.

wp-erp (15)

Closes #1590 — [FREE] Long / load-more pages show a white gap below the fold
Closes #1591 — [FREE] Years of Service report: Load more button missing on long lists
Closes #1592 — [FREE] Employee table name subtitle shows Employee ID instead of department
Closes #1595 — [FREE] Leave Entitlements list: fix zero Spent + missing Available column
Closes #1596 — [FREE] Leave Requests: require reject reason, fix year-scoped counts, restore filters
Closes #1597 — [FREE] Dashboard: next-month Who's-Out, About-to-End widget, half-day + announcement read-state
Closes #1598 — [FREE] Holidays + Departments: range flag, list totals, >100-row pagination
Closes #1599 — [FREE] Employees + Profile: Employment Type column, education edit-prefill, country dropdowns
Closes #1600 — [FREE] HR React in WordPress.org CI build + version constant
Closes #1601 — [FREE] HR React: skeleton loading on all list + report pages
Closes #1602 — [FREE] Employee profile privacy gating + peer view (v1–v4)
Closes #1603 — [FREE] Departments/Designations bulk delete + employee validation guards
Closes #1604 — [FREE] Table first-column left padding (no-checkbox tables)
Closes #1607 — [FREE] HR topbar nav capabilities follow legacy menu gates
Closes #1608 — [FREE] People > Requests: per-type tab counts + counts endpoint

erp-pro [FREE] (79)

Closes wp-erp/erp-pro#575 — [FREE] Employee profile v4 as canonical main + grid view
Closes wp-erp/erp-pro#576 — [FREE] Leave form parity fixes — FY drop, document attach, policy fields
Closes wp-erp/erp-pro#577 — [FREE] Org Chart + top-nav/submenu parity
Closes wp-erp/erp-pro#578 — [FREE] Pro upsell/badge parity + advanced-leave field injection
Closes wp-erp/erp-pro#579 — [FREE] Parity gaps + dashboard grid + release pipeline (build/i18n/RTL)
Closes wp-erp/erp-pro#585 — [FREE] Leave-request bulk approve/reject/delete
Closes wp-erp/erp-pro#586 — [FREE] Leave-entitlement bulk delete
Closes wp-erp/erp-pro#587 — [FREE] HR Financial Years settings
Closes wp-erp/erp-pro#588 — [FREE] People > Requests tabbed aggregator
Closes wp-erp/erp-pro#589 — [FREE] Inline dependency quick-add
Closes wp-erp/erp-pro#590 — [FREE] Frontend validation + UI feedback + upload/KSES hardening
Closes wp-erp/erp-pro#597 — [FREE] Employee profile field-row icons (all 4 views)
Closes wp-erp/erp-pro#601 — [FREE] Sync all list tables to Employees-table box/padding
Closes wp-erp/erp-pro#603 — [FREE] Sync card title/box/divider + input/label to Add-New-Employee
Closes wp-erp/erp-pro#605 — [FREE] Leave-request reversal + leave-policy duplicate row actions
Closes wp-erp/erp-pro#609 — [FREE] Employee profile/create tab vertical-rhythm spacing
Closes wp-erp/erp-pro#618 — [FREE] Org Chart — filter on top, full-width chart, crop fix
Closes wp-erp/erp-pro#623 — [FREE] HR sidebar nav icons — match Figma
Closes wp-erp/erp-pro#624 — [FREE] Org Chart — top-down layout restore
Closes wp-erp/erp-pro#627 — [FREE] Employee photo shown as avatar app-wide
Closes wp-erp/erp-pro#628 — [FREE] Collapsed sidebar — submenu hover flyout
Closes wp-erp/erp-pro#629 — [FREE] Reports — load more (Headcount + Years of Service)
Closes wp-erp/erp-pro#641 — [FREE] First-run "new HR experience" admin notice
Closes wp-erp/erp-pro#698 — [FREE] Make the legacy HR redesign banner dismissible
Closes wp-erp/erp-pro#700 — [FREE] Thinner pro top bar + full-width panels
Closes wp-erp/erp-pro#704 — [FREE] HR Dashboard React refactor — decompose DashboardPage, drop dead widget
Closes wp-erp/erp-pro#705 — [FREE] Leave Requests React refactor — extract table + filters, status-column parity
Closes wp-erp/erp-pro#706 — [FREE] Employee form React refactor — extract pure validation/payload logic
Closes wp-erp/erp-pro#707 — [FREE] Decompose employee profile layouts (v1–v4) into section components
Closes wp-erp/erp-pro#708 — [FREE] Decompose 10 HR list/feature pages into table/toolbar/filter components
Closes wp-erp/erp-pro#709 — [FREE] Decompose EmployeeForm, single-employee page & form dialogs
Closes wp-erp/erp-pro#710 — [FREE] Decompose dashboard & leave-requests pages
Closes wp-erp/erp-pro#711 — [FREE] HR redesign UX: dismissible banner, thinner top bar, full-width panels
Closes wp-erp/erp-pro#720 — [FREE] HRM React: RTL support + localized DatePicker
Closes wp-erp/erp-pro#722 — [FREE] HRM React i18n: fix empty-value clobber in translation merge
Closes wp-erp/erp-pro#725 — [FREE] Bug: HR React dashboard/stat zero renders slashed (Mona Sans tabular figures)
Closes wp-erp/erp-pro#733 — [FREE] HR React: leave/employee parity + role-gated add-new + dialog fixes
Closes wp-erp/erp-pro#735 — [FREE] Bug: leave calendar inaccessible to employees (own-scope view)
Closes wp-erp/erp-pro#736 — [FREE] Bug: employee self-edit 403 + field-level access parity
Closes wp-erp/erp-pro#737 — [FREE] Feature: Terminate + Print actions on employee profile header
Closes wp-erp/erp-pro#738 — [FREE] Bug: employee CSV import missing column→field matching step
Closes wp-erp/erp-pro#743 — [FREE] Enhancement: Inter as the HR React admin UI font
Closes wp-erp/erp-pro#749 — [FREE] HR React design-system sync — request tables, dashboard, tokens
Closes wp-erp/erp-pro#752 — [FREE] HR React design-system consistency sweep — components, buttons, filters, tables, spacing
Closes wp-erp/erp-pro#755 — [FREE] HR React date-range validation, status pill radius, mb-4, calendar tsc fix
Closes wp-erp/erp-pro#759 — [FREE] Employee profile v4 header polish — name/badge align, full border, full-width divider
Closes wp-erp/erp-pro#762 — [FREE] Dark-mode sharper borders + 'View legacy version' label
Closes wp-erp/erp-pro#767 — [FREE] HRM React — Vue→React parity gaps (leave, profile, calendar, reports, announcements)
Closes wp-erp/erp-pro#769 — [FREE] HRM React — post-audit UX fixes (employee filter, switch-to, bulk bars)
Closes wp-erp/erp-pro#771 — [FREE] HRM React: close remaining Vue→React parity gaps
Closes wp-erp/erp-pro#776 — [FREE] Preset date-range picker for HR filters
Closes wp-erp/erp-pro#778 — [FREE] Scope filter for top-bar global search
Closes wp-erp/erp-pro#779 — [FREE+PRO] Release build robustness + fast mode
Closes wp-erp/erp-pro#780 — [FREE+PRO] HR redesign switch moved to admin footer (no top banner)
Closes wp-erp/erp-pro#788 — [FREE] Bug: HR React report tables — rounded corners clipped
Closes wp-erp/erp-pro#789 — [FREE] Enhancement: employee profile-view tables synced to report-card design
Closes wp-erp/erp-pro#791 — [FREE] Feature: profile photo upload/delete on Add/Edit Employee + profile-v4 header

Closes wp-erp/erp-pro#833 — [FREE] Bug: People → Requests loses the active tab on refresh

Closes wp-erp/erp-pro#812 — [FREE] Top bar — search + theme toggle in one bordered group
Closes wp-erp/erp-pro#813 — [FREE] Dashboard — medium Team Calendar with named leave chips + card/widget polish
Closes wp-erp/erp-pro#816 — [FREE] Dashboard cards — designation, holiday badges, announcement excerpt + persisted birthday wish
Closes wp-erp/erp-pro#817 — [FREE] Top bar — remove wide nav gap on small screens
Closes wp-erp/erp-pro#821 — [FREE] Dashboard + announcement capability gating (employee parity)
Closes wp-erp/erp-pro#824 — [FREE] Dashboard — remove employee-view whitespace (single grid, calendar spans 2 rows)
Closes wp-erp/erp-pro#828 — [FREE] HR dashboard layout redesign — attendance rail + flexible Figma card rows
Closes wp-erp/erp-pro#829 — [FREE] Employee leave self-service "Take a Leave" + leave calendar avatars
Closes wp-erp/erp-pro#830 — [FREE] Leave calendar polish — striped holidays, Pending pill, sparse-month border fix
Closes wp-erp/erp-pro#842 — [FREE] HR React: calendar dates render a day off when browser and site timezones differ
Closes wp-erp/erp-pro#860 — [FREE] Employee add/edit parity + setup-screen links on lookup selects
Closes wp-erp/erp-pro#848 — [FREE] Bug: HR React segmented controls bleed past their border; employee name not linked
Closes wp-erp/erp-pro#851 — [FREE] Enhancement: gate the HR React redesign on install age (new installs only)
Closes wp-erp/erp-pro#852 — [FREE] Enhancement: put every HR React date on the site clock + calendar month/year dropdowns
Closes wp-erp/erp-pro#855 — [FREE] Enhancement: fade dashboard cards and every list table up as a page paints
Closes wp-erp/erp-pro#858 — [FREE] Inline "+ Add new" on the employee-form Location field
Closes wp-erp/erp-pro#859 — [FREE] Inline "+ Add New" on Financial Year in the New Leave Request dialog
Closes wp-erp/erp-pro#862 — [FREE] Inline "+ Add New" on Leave Policy in the New Leave Request dialog

Closes wp-erp/erp-pro#866 — [FREE] Bug: Country / Nationality dropdown renders a raw HTML entity (&#197;land Islands)
Closes wp-erp/erp-pro#868 — [FREE] Bug: HR React date picker has no year selector — only month-by-month arrows
Closes wp-erp/erp-pro#867 — [FREE] Tracking: HRM React admin redesign QA cycle — 76 defects, 73 fixed + verified (free F01–F13, pro P01–P11)

QA cycle fixes (erp-pro#867)

Fresh-install manual QA, free-only with pro absent, every finding confirmed in MySQL and
re-verified in the browser after a rebuild:

Defect Fix
Empty employee form showed no error at all — native required aborted submit before the React summary ran noValidate on the form; one validator owns the reporting
Escape with a select popup open closed the whole dialog and discarded the form new shared/utils/dialog.ts → dismissGuard(), applied to 5 dialogs
"Reporting To" prefilled a literal 0 id-bearing keys collapse zero to empty (pay_rate 0 untouched)
Save Changes landed on the previous page closeAfterSave() → /employees/:id; Cancel keeps history-back
17–19 Aug holiday rendered "Aug 17 – Aug 20" (and in the dashboard widget) shared RestControllerV2::cast_calendar_day() — send the calendar day, not an instant
Financial-year created_at/updated_at stored 2026 unix timestamp with %d in erp_settings_save_leave_years()
Duplicate policy assign reported success and closed silently controller counts skipped; the page says so and keeps the dialog open
Head Count report: Job Title and Location blank job_title / work_location — the accessors that exist (legacy view fixed too)
Country list rendered &#197;land Islands decode country + state labels once in the boot payload
#/dashboard 404'd route alias to the index route
Departments/Designations list state lost on refresh new shared/hooks/useListUrlSync.ts

Test specification and per-area case files live in QA_manual_test/ — surface derived from the
route tables rather than from clicking around, one file per area with route · locator · API · DB ·
expected · observed, written so a Playwright spec can be lifted straight out.

Closes wp-erp/erp-pro#873 — [FREE][PRO] Security: peer contact details and custom-field values readable by any employee
Closes wp-erp/erp-pro#875 — [FREE] Silent data loss: React announcement save wipes the Push flag, policy edit discards changes
Closes wp-erp/erp-pro#876 — [FREE] An employee with no first/last name is invisible in every React picker
Closes wp-erp/erp-pro#877 — [FREE][PRO] Report data: empty Years of Service, zero absences, shift always "-"
Closes wp-erp/erp-pro#878 — [FREE] UI engine rollout: pro pages ignore the preference, switch writes prefs while forced
Closes wp-erp/erp-pro#879 — [FREE][PRO] Wrong routes and keys: reimbursement badge (0), #/reports 404, share pickers 404
Closes wp-erp/erp-pro#880 — [FREE][PRO] Form validation: self-edit impossible, required custom fields ignored, half-day books a range
Closes wp-erp/erp-pro#885 — Switching between the new and old HR UI always lands on the dashboard, not the page you were on
Closes wp-erp/erp-pro#886 — [FREE][PRO] Announcements can be sent as Push/SMS, and workflows can Send SMS
Closes wp-erp/erp-pro#887 — [PRO] Shift on the employee form, My Payslips on the dashboard, To-Do profile tab
Closes wp-erp/erp-pro#888 — [FREE][PRO] List and form parity: Reason Details, Trashed At, half-day period, Hired filter, nav shortcuts
Closes wp-erp/erp-pro#889 — [FREE][PRO] Attendance per-day detail, payroll bulk filters, training month filter + View page
Closes wp-erp/erp-pro#890 — [FREE] Announcements: read-only View, scheduling with a Scheduled tab, editable holiday import
Closes wp-erp/erp-pro#891 — [FREE][PRO] One heading level and one header rule across every employee-profile card
Closes wp-erp/erp-pro#892 — [FREE][PRO] Cosmetics and PHP notices: date formats, aliases, toast filler, Undefined property
Closes wp-erp/erp-pro#893 — [FREE] Release zips must come from grunt; the stale-classmap warning is an install artefact
Closes wp-erp/erp-pro#894 — [FREE][PRO] §B parity triage closed: 28 shipped, 16 claims refuted, 0 left
Closes wp-erp/erp-pro#895 — [FREE] Engine switch keeps your place in both directions

Closes wp-erp/erp-pro#899 — [FREE] Radio option labels are unstyleable and not clickable in erp_html_form_input()

Closes wp-erp/erp-pro#902 — [FREE] Employee pickers show a bare name where the Employees table shows a photo

Closes wp-erp/erp-pro#904 — [FREE] HR New UI calls erp/v2/requests/counts 7-8 times on every page load
Closes wp-erp/erp-pro#905 — [FREE] Non-existent employee id returns 200 and renders a phantom profile with Edit/Terminate
Closes wp-erp/erp-pro#908 — [FREE] HR dashboard "About to End" lists contracts that ALREADY ENDED and hides the ones ending soon
Closes wp-erp/erp-pro#909 — [FREE] Employee dashboard shows DRAFT and TRASHED announcements

Closes wp-erp/erp-pro#910 — [FREE][PRO] Bulk actions appear in three different places depending on the screen
Closes wp-erp/erp-pro#911 — [FREE][PRO] Bulk Reject looks identical to Approve — two opposite outcomes, same button

Closes wp-erp/erp-pro#985
[FREE] The employee photo-upload endpoint no longer accepts a logged-out visitor (69d0a7c)

Closes wp-erp/erp-pro#1010
Closes wp-erp/erp-pro#1012
Closes wp-erp/erp-pro#1013
Closes wp-erp/erp-pro#1014
Closes wp-erp/erp-pro#1007
Closes wp-erp/erp-pro#1008
Closes wp-erp/erp-pro#1015
Closes wp-erp/erp-pro#1016
Closes wp-erp/erp-pro#954
Closes wp-erp/erp-pro#424
Closes wp-erp/erp-pro#953
Closes wp-erp/erp-pro#1006
Closes wp-erp/erp-pro#1009
Closes wp-erp/erp-pro#1011
Closes wp-erp/erp-pro#1021
Closes wp-erp/erp-pro#1022
Closes wp-erp/erp-pro#1024
Closes wp-erp/erp-pro#1025
Closes wp-erp/erp-pro#1026
Closes wp-erp/erp-pro#1027

Closes wp-erp/erp-pro#1034
Closes wp-erp/erp-pro#1035
Closes wp-erp/erp-pro#1036

Closes wp-erp/erp-pro#1040
Closes wp-erp/erp-pro#1041
Closes wp-erp/erp-pro#1042
Closes wp-erp/erp-pro#1047
Closes wp-erp/erp-pro#1048
Closes wp-erp/erp-pro#1049
Closes wp-erp/erp-pro#1050
Closes wp-erp/erp-pro#1052
Closes wp-erp/erp-pro#1055
Closes wp-erp/erp-pro#1056
Closes wp-erp/erp-pro#1058
Closes wp-erp/erp-pro#1062
Closes wp-erp/erp-pro#1065
Closes wp-erp/erp-pro#1066
Closes wp-erp/erp-pro#1068
Closes wp-erp/erp-pro#1069
Closes wp-erp/erp-pro#1071
Closes wp-erp/erp-pro#1072
Closes wp-erp/erp-pro#1073
Closes wp-erp/erp-pro#1075

@arifulhoque7 arifulhoque7 self-assigned this Jun 16, 2026
arifulhoque7 and others added 28 commits June 17, 2026 17:04
useRequestsPendingCount() reads pending_total from /erp/v2/requests/counts
(module-cached, one fetch) and NavDropdown renders a destructive count badge on
the people-requests child — restores the legacy Requests count badge.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SeedAssets wrote allottable='yes', but the asset code (legacy + v2
get_items_by_group, asset_get_item_by_group) filters on allottable='on' — the
checkbox value save_asset stores. So seeded assets never matched, leaving the
Approve/Allot "Item" dropdown empty. Fixed the seed to 'on'.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
# Conflicts:
#	.github/workflows/deploy.yml
#	.gitignore
#	wp-erp.php
deploy-erp.yml (upstream, tag-triggered wp.org deploy) lacked the Node 24 +
pnpm React build, so the shipped zip had no React HR UI. Port the build:react
step in and remove the redundant release-triggered deploy.yml.
- Top bar 64px → 48px, tighter nav padding/icons, slim logo
- Sidebar top strip 64px → 48px
- Main panel vertical padding reduced (lg:py-12 → lg:py-6), px kept
- All page wrappers max-w-7xl → max-w-full for full-width content

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a close (×) button to the 'We've redesigned the HR experience'
switch banner; dismissal persists per-browser via localStorage and the
banner stays hidden on subsequent loads. Also clear: both so the banner
drops below WordPress's floated Screen Options tab instead of overlapping it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…widget

No behaviour or visual change. Split the 746-line DashboardPage.tsx into
focused modules: DashboardCards.tsx (layout primitives), DashboardWidgets.tsx
(data rows + ProWidget) and format.ts (greeting/date/initials helpers);
DashboardPage now only does data loading + page composition. Remove the dead,
never-mounted LeaveCalendarWidget.tsx (zero imports; the dashboard uses
MiniCalendarWidget). tsc/lint clean on the changed files, free React build green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…arity

No behaviour change. Split the 664-line LeaveRequestsPage into focused
siblings: LeaveRequestsTable.tsx (presentational row grid + StatusPill/fmt)
and LeaveRequestsFilters.tsx (controlled secondary filter row); the page now
owns only query/selection state and the moderate/delete/bulk flows. Add
whitespace-nowrap to the status + header cells so the status badge never wraps
and columns scroll like the Employees table. Also rename the tab-map's
shadowing 'selected' to 'isSelected'. tsc clean, free React build green; the
16 remaining lint findings are pre-existing patterns moved verbatim (nested
ternaries, sprintf translator comments, label-wraps-control).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
No behaviour change. Move the field-rule constants, validateEmployeeForm and
buildEmployeePayload out of EmployeeForm.tsx into a pure, unit-testable
validation.ts; the form now calls them and keeps only UI + wiring. FormState/
FormMode types move there too and are re-exported from EmployeeForm so existing
consumers are unaffected. tsc clean, free React build green; the 2 remaining
lint findings are pre-existing a11y notes in untouched Notification JSX.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Split the four single-employee profile pages (v1 read-only, v2 dark-pill,
v3 portrait grid, v4 sidebar) into flat sibling modules, same as the
dashboard / leave-requests / employee-create refactors. Each page keeps
only orchestration (data fetch, caps, tab state, body switch); chrome and
pure helpers move out:

  - ProfileHeader   header card / hero section
  - OverviewTab     overview body (detail cards + extra fields + general)
  - DetailCard      titled card + label/value row primitives
  - SideTab/PillTabs/ProfileTab  per-page tab nav
  - profile-format  pure str/labelOf/initials/statusVariant + shared types

Behavior is 1:1 — JSX, classes, comments and imports copied verbatim;
no API change. Both *Inner and *Page exports preserved (my-profile and
router consume them). Pages: 1931 -> 817 lines.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Split the remaining fat single-file pages into flat sibling modules, same
discipline as the dashboard / leave-requests / employee-profile refactors.
Each page keeps only orchestration (data fetch, state, dialog wiring);
table / toolbar / grid / pure helpers move out:

  holidays           Table, Toolbar, format
  departments        Table, Toolbar
  designations       Table, Toolbar
  leave-policies     Table, Filters
  leave-entitlements Table, Filters
  leave-types        Table
  leave-unpaid       Table, format
  announcements      Table, Toolbar, format
  leave-calendar     CalendarGrid, CalendarToolbar, format
  org-chart          OrgTree, OrgCard, DeptPill, format

Behavior 1:1 — JSX, classes, comments and imports copied verbatim; no API
change. Each page's single export is preserved (router lazy-imports them).
Pages: 3837 -> 2611 lines. Already-extracted siblings (form dialogs, shared
org primitives) left untouched. tsc clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Split the shared create/edit form body into flat sibling components, same
discipline as the dashboard / profile / list-page refactors. The form keeps
only state, effects and handlers (lookups, email user-check, quick-add
dept/desig, validate/build/submit); the markup moves out:

  EmployeeFormAlerts          submit/dependency/user-check/validation alerts
  EmployeeBasicSection        Basic Information fields + quick-add selects
  EmployeeWorkSection         Work fields (create/edit variants)
  EmployeePersonalSection     Personal Details + address fields
  EmployeeNotificationSection create-only welcome-email opt-ins

Behavior 1:1 — JSX, classes, comments copied verbatim, wired through props;
ExtraFields interleaves stay in the orchestrator. EmployeeForm + the
FormMode/FormState re-exports preserved. 977 -> 478 lines. tsc clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Finish the React decompose sweep — split the last monolithic files into
flat sibling modules, same discipline as the rest of the series. Pages/
dialogs keep state, effects and handlers; markup + pure helpers move out:

  EmployeeSinglePage    SingleHeader, SingleOverview, SingleNavMenu,
                        SingleDetailCard, single-format
  LeavePolicyFormDialog PrimaryFields, ScopeFields, Toggles, form-helpers
  JobUpdateDialog (v4)  JobStatus/Info/Type/Compensation fields, helpers
  NewLeaveRequestDialog NewLeaveRequestForm, helpers

Behavior 1:1 — JSX, classes, comments copied verbatim, wired through props.
All exports + their props preserved (barrels, router and page consumers
unchanged). tsc clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Final free-side decompose pass — split two remaining dialogs into flat
sibling modules:

  EmployeeImportDialog (employees)  Upload + Result views, import helpers
  JobUpdateDialog (employee-create) JobStatus/Type/Compensation/Info fields,
                                    job-update-helpers

Behavior 1:1 — JSX, classes, comments copied verbatim, wired through props.
Exports + props preserved (toolbar / job-tab consumers unchanged). tsc clean,
build green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Mirror directional lucide icons + fix nav dropdown right-anchor under RTL
- Replace native date inputs with plugin-ui DatePicker (WP-locale + RTL
  calendar) via a shared DateField; delegate TextField's date branch
- Port react-day-picker slide keyframes into main.css (rtl:ignore) so the
  month-nav no longer leaves a duplicate month stacked
- Strip #wpcontent inline-start padding (logical) so the app sits flush in RTL
- Add committed makejson:react i18n step (wp i18n make-json + chunk merge)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
merge-react-json.php overwrote a translated string with an empty value
from another code-split chunk (arbitrary glob order → last-write-wins),
silently dropping most translations from the entry-handle JSON. Skip
empty/blank values so every real translation survives consolidation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mona Sans renders tabular figures with a slashed zero; the default
proportional figures use a plain zero. Removing the tabular-nums utility
from the HR React stat numbers makes the "0" glyph match the Project
Manager dashboard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ork UI

Validation + show/update parity with legacy across HR React:
- leave: central New Request now uploads the document (multipart via apiFetch
  body) + reason required; apiFetch gains a raw `body` option for FormData
- employee: education interest/expiration_date inputs; profile shows
  spouse/driver-license/hobbies; Job-Info no longer over-validates (date only)
- "+ Add new" department/job-title gated by erp_manage_department /
  erp_manage_designation (add + edit forms + Job dialog)
- delete buttons: red border on outline bulk-delete (leave/dept/desig/entitlement)
- request dialogs get max-h + scroll (no more cut-off New Leave Request)

Resignation + Remote Work (were React placeholders):
- People → Requests tabs (list + approve/reject/delete + file-on-behalf)
- /my-profile self-service apply (Resign red, Remote Work) beside Edit, with
  the employee's own request history + status

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The `/leave/calendar` route was gated `erp_leave_manage`, but the v2
LeaveCalendarController already serves any logged-in user their own
leaves. Lower the route cap to `[]`, drop the page's manager CapabilityGate,
and scope a non-manager to `me` with the department/designation filters
hidden ("My Leave Calendar"). Managers keep the whole-company `all` view.
The dashboard MiniCalendar already links here, so employees now reach it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rity

Two legacy-parity gaps in the React employee edit path:

1. Self-edit 403: `EmployeesControllerV2::permission_edit_employee()` used a
   bare `erp_edit_employee` (no target id), so an employee editing their own
   profile (and a department lead a report) got 403 on save/avatar — the UI
   already let them open the editor. Meta-map it against `user_id` to match
   the legacy AJAX gate. Reactivate stays manager-only (new callback).

2. Field-level: Employee ID and the job/org/compensation fields are
   manager-only in the legacy form but were editable by a self-editor in
   React. Disable them in EmployeeBasicSection + hide the Work section for a
   self-editor (UI), and strip those keys server-side in `update_item` when
   the editor lacks the bare manager cap (defends against crafted requests).
   `date_of_birth` stays self-editable. `SmartSelectField` gains `disabled`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The legacy single.php Actions panel had Edit | Terminate | Print; the React
v4 profile header had only Edit. Add Print (window.print()) and Terminate to
ProfileHeader. Terminate is gated `erp_can_terminate`, hidden for self and
already-terminated employees, and reuses the existing TerminateDialog wired
to the store `terminateEmployee` action (POST erp/v2/employees/{id}/terminate).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The React importer auto-matched only exact header names and silently
dropped the rest — a CSV with differently-named columns couldn't be
imported (the legacy tool had a field-mapping table). Add the missing step:
after upload, every CSV column is shown with an ERP-field select, auto-
matched by header (key/label/alias-aware) and editable, with a "Don't
import" option and duplicate-target prevention. Required fields (first
name, last name, email) must be mapped before import; rows are built from
the confirmed mapping instead of header-name detection.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Restyle the column-matching step to the HRM-Redesign-2024 design: a card
with a centered "Map Properties" title, a "Columns (N) | Profile Field"
header row, one row per CSV column (name + red asterisk when mapped to a
required field) with a full-width chevron select, and an "Import Employee"
action label. Drops the per-column sample value + arrow glyph.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ping

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Token-consistent table headers (white thead, 12px/400/uppercase #828282 titles,
not bold), count numbers #A5A5AA, bare 20px filter icon (single muted colour),
list/grid view toggle moved to its own row above the card, and standard buttons
normalised to the 40px Add-button size.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Approving a request whose leave year ended before the current one is
refused ("You can not modify past leave year requests"), but the v2
create accepted it, so the request sat pending for good. The create now
refuses it with the same message; current and future years are
unchanged.
…lf-day can be turned off

Advanced Leave's policy insert filters read their fields off $_POST and
write 0 for any that are missing, so a v2 update without extra (or with
only some keys) reset accrual, carry-forward and encashment. The update
now fills in the stored values for whatever the request does not send;
anything sent still wins.

The half-day filter only ever sets the flag on, so once enabled it could
never be switched off again. The v2 update clears it when the form sends
it off. The legacy filters are unchanged.
Workflow and Custom Fields save the View legacy version choice under
their own slug, but the resolver only read the dashboard key for any
non-HR page, so the choice was never read and the page stayed on the
new UI. A page's own saved choice now wins; without one it follows the
HR-wide dashboard choice, as before.

Refs wp-erp/erp-pro#1055
…t serves clarity

Switching to the new HR design now opens a full-screen welcome once per
user (set by the switch, consumed by the boot payload; the old notice is
marked read so the two never stack). The page waits behind it and plays
its entrance when it closes.

One easing family and timeline across the app: sections cascade, cards
stagger, each dashboard number counts up after its card lands, charts
draw last. A number that changes later counts from the old value and its
card rings once. Dialogs, sheets, menus, selects, popovers and tooltips
open and close with short eased motion keyed on plugin-ui's data-slot
and data-open attributes (its animate classes are not in the build).
Rows fade in top to bottom, buttons press, touch devices get a 10ms
haptic tick, and headings rise out of a soft blur like the welcome's.
Reduced motion turns it all off.
…ge away

plugin-ui's searchable select calls scrollIntoView() on the chosen option
before its popup is positioned, while the option still sits at the top
of the document. Base UI's scroll lock only stops the reader scrolling,
not code, so the page under the dialog jumped to the top and stayed
there after the dialog closed. While the lock is on, any scroll of the
page itself is now put back before the next paint.
…text lines up

The page-size empty state was 17px taller than a 900px window, so an
empty list showed a scrollbar with nothing to scroll; its padding is
trimmed. The Export Employee dialog's description was centred under a
left-aligned title; it now aligns left.
…ncluded

Every scroll area kept a 7px blue bar on show, and dialogs, menus and
selects, which portal outside #erp-hr-app, drew the browser's default
bar. Now every scroll area, overlays included, keeps a thin bar whose
thumb appears only while the pointer is over it; .scrollbar-none strips
still hide theirs. Built on scrollbar-width / scrollbar-color, since
Chrome does not repaint a ::-webkit-scrollbar-thumb on :hover alone.
… does nothing

plugin-ui's simple toolbar draws an AI Assist button with no handler, so
pressing it on the announcement and pro email editors did nothing. It is
hidden, with its divider, until the editor gives it a job.
…eaves

The Unpaid Leaves table printed the total as the raw number from the API
(49382, or 11333.333...), while Forward Leaves shows money to two
decimals. The total now uses the same two-decimal format.
…t lifts

The ring sat on the outer link while the hover lift moved the inner card,
so after a click or on keyboard focus the card covered the ring's top and
left a gap at the bottom. Draw the ring on the card and lift it on focus.
PHP 8.4 deprecates fputcsv() without the escape argument. Pass the
current defaults (',', '"', '\\') so exports are byte-for-byte the same
and nothing is printed on 8.4. Works unchanged on PHP 7.4.
The v2 employee create and edit checked department, job title and
location but stored any reporting_to id, including one with no employee
record and the employee themselves.
- Create, edit and profile Back return to a WP ERP screen, never past it.
- Update Job Information opens on the current values; the profile header refreshes after a job change.
- Bulk deletes ignore non-positive ids instead of flipping -1 to 1.
- Holiday end dates must be real and a holiday spans at most a year.
- A new leave policy needs at least one day.
- The balance card total leaves out days handed back by a rejected approval.
- A leave reason beyond its column is a 400, not a 500.
- A job history change cannot be dated ahead.
…labels wrap

- Employees by Department grows with the department count and labels every bar; a fixed 260px dropped every other name.
- The leave requests Reason column keeps a minimum width instead of shrinking to nothing.
- Profile stat labels wrap to two lines instead of cutting off the unit.
Trashing an employee keeps status = 'active' and only sets deleted_at.
The department bars, the headcount total and list, salary history and
years of service filtered on status alone, so a trashed employee still
counted there while the dashboard total and the department list dropped
them. The org chart offered "No Team" for terminated or trashed rows
it then showed nothing for.
…n never be decided

A terminated or trashed HR manager kept the erp_hr_manager role, so every
manager check still passed with their old session; a terminated or trashed
department lead kept leave moderation and review rights; a terminated line
manager kept reading notes. erp_hr_is_offboarded_user() now decides it in
one place (the cap filter, the lead helpers, the line-manager mapping), with
site administrators left alone. An HR manager can no longer terminate,
delete or re-photo an administrator's record.

Leave: overlapping pending/approved requests are refused (409); a request
in an ended year is refused even with no current leave year; rejecting an
approved request clears the unpaid ledger of every approval record; the
calendar shows leave that started before the window; the list ignores a
date filter that is not a real date; holiday start dates are checked whole.

Refs wp-erp/erp-pro#1062
wp-erp is a public repository, so workflow artifacts can be downloaded
by anyone signed in to GitHub while they are kept. The E2E/API workflow
uploaded the whole erp-pro tree as `erp-pro-build` on every push to
develop and every nightly run.

`build_pro` now only checks that the erp-pro secret works (a bad token
still turns the run red), and the e2e and api jobs check erp-pro out
themselves with the same secret, with persisted credentials off. Fork
pull requests still run lite.
`erp_hr_leave_insert_request()` now refuses an entitlement that belongs
to another employee: any entitlement id was accepted, so a colleague's
balance could be spent.

Two helpers hold the rules the v2 employee leave route applied on its
own, so the v1 leave routes (served by WP ERP Pro) apply the same ones:
`erp_hr_leave_overlapping_request_id()` (a pending, forwarded or
approved request on the same days, optionally leaving out the request
being edited) and `erp_hr_leave_is_past_year()` (a leave year that no
longer takes requests).

Refs wp-erp/erp-pro#1062
Someone who has left (terminated, resigned or trashed) kept every
self-service meta cap on their own record except edit: they could still
file leave and add or change experience, education, dependents and
documents. Every self-service cap other than `erp_view_*` is now
refused once they are offboarded.

Reading stays: the v2 profile sections (experiences, educations,
dependents) and the employee leave routes also answer the employee
reading their own record with `erp_view_employee`, so a leaver keeps
read-only access. Writes keep the edit gate.

Refs wp-erp/erp-pro#1062
`GET /erp/v1/utility/get-active-plugins` returned every active plugin
to any caller. Without `activate_plugins` it now lists WP ERP's own
plugins only.

Refs wp-erp/erp-pro#1062
WP ERP's own screens use `erp/v2`. The HR `erp/v1` routes are used by
Pro features (the ERP mobile app and the HR Frontend portal), so their
11 controllers (Employees, Departments, Designations, Birthdays,
Reports, Leave Entitlements, Holidays, Policies and Requests,
Announcements, Company) now live in WP ERP Pro, which serves the same
paths, arguments and responses, hardened to the v2 rules.

`erp_hrm_rest_api_controllers` is still applied, to an empty list, for
code that adds controllers through it.

Breaking: on a site running WP ERP without Pro the HR v1 routes are
gone, and a site that updates WP ERP before Pro loses them until Pro is
updated.
`Enqueue::boot_payload()` returns the `__ERP_HR_BOOT__` payload for an
HR page, so WP ERP Pro's HR Frontend portal, which mounts its own build
of the HR app outside wp-admin, gets the same capabilities and settings
without copying how they are built.
`GET /erp/v1/accounting/v1/employees`, `/employees/{id}` and
`/employees/{id}/transactions` were open to every employee
(`erp_view_list`), and returned each employee's full HR record (date of
birth, marital status, pay rate and type, contact details, address,
family names) and their ledger. They serve the Accounting > People >
Employees screen, which needs `erp_ac_manager`, so the routes now need
it too.

Refs wp-erp/erp-pro#1062
Every PHP file lints on 7.4, 8.0, 8.1, 8.2, 8.3, 8.4 and 8.5,
PHPCompatibilityWP (testVersion 7.4-) reports nothing, and every GET
route under /erp/ now answers without a fatal error, notice or
deprecation from WP ERP code on each of those versions.

PHP 8 fatal errors (warnings on 7.4), all on a record that does not
exist:
- accounting people/{id}: assigned properties on `false`; now 404.
- accounting people/{id}/address and purchases/{id}: array_keys() on
  null in the address formatter.
- accounting tax-rate-names/{id}: array_merge() on null.
- accounting people/check-email: in_array() on null people types (a
  WP_Error was read as a person).

Fatal on every version:
- CRM contacts: prepare_item_for_response() started with a leftover
  wp_send_json() from 2020, so every contacts response printed the raw
  row (internal hash included) and stopped.
- CRM activities/{id} and contacts/groups/{id} for a missing record
  called toArray() on null.
- CRM contacts/groups without `order`/`orderby` passed null to the
  query builder, which threw; order is now ASC or DESC and orderby a
  group column.
- accounting people/{id}/opening-balance calls a function that was
  never added; it now answers 501 instead of a fatal error.

Deprecations:
- `(boolean)` cast (8.5), `${var}` interpolation (8.2), stripslashes()
  of null (8.1), null used as an array key (8.5).

Bundled libraries: illuminate/* in vendor/ still raise "implicitly
nullable parameter" deprecations on PHP 8.4+; fixing them needs a
release that drops PHP 7.4.
…quired

The Work section is hidden from someone editing their own profile, but
a required custom field placed in it was still checked, so every
self-save stopped on an error for a field they cannot see or fill. The
check now skips Work fields under the same condition that hides them;
the server already ignores Work fields in a self-edit.

Refs wp-erp/erp-pro#1064
pnpm 11+ reads its settings only from pnpm-workspace.yaml, so the
`pnpm.onlyBuiltDependencies` list in package.json is ignored and
`bin/build-react.sh` stopped on ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED for
the git-hosted @wedevs/plugin-ui. The new workspace file allowlists it
and keeps the old default (no script) for the rest, including esbuild,
whose binary comes from its optional platform package.

The release zip now leaves out the pnpm lock and workspace files with
the other HRM build tooling.
Server (V2 REST and helpers):
- Role cap sync no longer re-grants a cap a site owner removed or set to
  false; it stores the last map and only adds caps that are new since.
- An offboarded HR manager is no longer reported as a manager, and an
  offboarded employee no longer reads the department, designation and
  search lists (staff names and avatars). Their own record still loads.
- Leave cannot be filed for a trashed employee, and validate-dates now
  checks pending overlaps the same way submit does (no 200 then 409).
- Saving a profile row with another employee's row id returns 404
  instead of quietly creating a new row.
- PATCH /employees/{id} no longer requires name and email.
- Holiday search escapes LIKE wildcards, the import preview uses the
  site year, and an out-of-range year returns 400.
- The boot payload can skip consuming the welcome flag (the HR Frontend
  portal uses it) and carries a nonce'd logoutUrl.

React admin:
- Calendar dates (leave, holidays, job history, reports) no longer show
  one day early when the browser timezone is ahead of the site.
- List errors show the server's message instead of "Unknown error", and
  an expired nonce on a list call refreshes like other requests do.
- Bulk approve/reject/delete, leave type and entitlement bulk delete and
  holiday import report partial failures instead of plain success.
- Employees only see the All and Active status tabs; HR sees all.
- Pending requests badge refreshes after moderation; the Available
  column is no longer sortable (the server cannot sort by it).
- Resignation and remote work deletes ask for confirmation; Main
  Location (id 0) is a selectable filter; failed lookups are retried.
- My Requests shows translated statuses and formatted dates; age and
  tenure use local dates; nested extra fields serialize correctly.
- Log out uses a nonce'd URL, and a leaver no longer gets a Back to
  People link to a page they cannot open.

Tests and CI: date and CSV specs cover the timezone case; Playwright
records no traces or videos on Pro runs.
- Filing leave now takes a per-employee lock around the overlap check
  and the insert, so two submissions sent at the same moment can no
  longer both pass and file the same days twice (measured: four
  concurrent requests now give one 201 and three 409s). The lock helpers
  are shared with the v1 routes in Pro.
- Deleting a leave type that no longer exists returns an error instead
  of a fatal "delete() on null", so a bulk delete with a stale id lists
  it as skipped; the none-deleted message now says why.
- The HRM v1 API specs are tagged @Pro: those routes ship with WP ERP
  Pro, so a Lite run no longer fails on them.
- Employees list and grid: row checkboxes and select-all appear only
  when the viewer has a bulk action (employees and department leads had
  checkboxes that selected rows nothing could act on).
- Leave requests: a department lead no longer sees Approve/Reject on
  their own request (the server refuses it); team requests keep them.
- People row Edit also shows on your own row, which the server allows.
- Take a Leave, Resign and Remote Work need an employee record (an
  administrator without one got a 400).
- A department lead can read the reviews they may already write and
  delete for their department: the performance read route and the
  per-employee capability map now agree with the write routes.
Employees:
- Announcements: a read-only list of the announcements addressed to
  you (GET /erp/v2/me/announcements, own published rows only, kses'd
  body, unread count), opened in the existing dialog and marked read; a
  top-bar bell with an unread dot and the latest five; a "View all" link
  on the dashboard widget. Managers keep the management page.
- People shows Active, Terminated, Resigned, Deceased and All (Trash and
  Inactive stay HR-only), and a former colleague's profile opens, with
  the same directory fields the list shows.
- A colleague's profile shows what the legacy admin view showed: basic
  info (names, employee ID, email) plus type, status, hire date,
  department and job title. No contact card, location, reporting line or
  custom fields; the single-employee API sends a colleague only those.
- Leave balances show Taken (approved days up to today) and Scheduled
  (approved days still ahead): taken + scheduled = spent, entitled -
  spent = left. Legacy showed Scheduled but always sent 0.
- Your own profile shows your shift (filled by the attendance module
  through the new erp_hr_v2_employee_item filter).
- The user menu names your role (Administrator, HR Manager or your
  designation).

Everyone: compensation pay rates are hidden with a per-row show toggle,
as the legacy Job tab did; #/reports is an index of the report pages
you can open; empty custom-field sections are hidden.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment