Repository navigation
HRM React admin redesign (free) - #1589
Open
arifulhoque7 wants to merge 344 commits into
Open
arifulhoque7 wants to merge 344 commits into
arifulhoque7 wants to merge 344 commits into
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:
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-valuespan, matching themulticheckboxbranch beside it; also drops a stray quote that emitted an invalid attribute and a duplicated sharedid.Notes
dist-reactis gitignored.🤖 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-09Baseline 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.
Release train: what is actually in scope
wp-erp/wp-erp(free)erp/v2, engine switch. HRerp/v1routes removed from free (now in Pro).wp-erp/erp-pro(pro)erp/v2controllers,@erp/hr-shared, HR Frontend portal rebuilt as its own React app, HRerp/v1routes (moved from free, hardened like v2)./accounting/v1/employees*now needserp_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
#/payroll/payruns,#/employees/{id}?tab=leave): the same view must come back.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
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
Advanced Leave (pro): half-day, accrual, segregation, unpaid leave, multi-level approval, carry forward and encashment.
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
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
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
Regression risk: legacy recruitment admin, application emails, CV downloads.
7. Documents, Training, Assets, Reimbursement (pro)
How to test
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
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
page=erp-workflowand Custom Fields.Regression risk: legacy screens untouched (CSS is scoped to
#erp-hr-appand 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
wp-admin,wp-jsonor 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./<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.shbuildshr-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/v1REST 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
/wp-json/erp/v1/hrm/*behave as before for allowed actions; as an employee,PUT /erp/v1/hrm/employees/<self>withpay_rate/department/statusleaves them unchanged, a login email change is 403, another employee's record is the limited peer card./wp-json/erp/v1/hrm/employeesis 404 (breaking for free-only integrations, see blocker 10); free's HR screens are unaffected.GET /wp-json/erp/v1/accounting/v1/employeesis 403 (it returned every employee's HR record and ledger).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.wp-jsonrefused, old slug stops working after a change), title, logo, login redirect for employees.Improvements: verify behaviour
YYYY-MM-DDon those screensFixes: confirm resolved + regression
/accounting/v1/employees,/{id},/{id}/transactions) gave every employee each employee's full HR record and ledgerGET /erp/v1/hrm/employees: colleagues' rows carry directory fields onlyplaywright.ymlrun: noerp-pro-buildartifact(boolean)cast,${var},stripslashes(null), null array key)WP_DEBUGon PHP 8.4: nothing fromplugins/wp-erp/outsidevendor/in debug.logsplit()(removed in PHP 7.0): sending was a fatal error on every supported PHPemployees/{id}/experiences/{id}always 500WP_DEBUGon PHP 8.4: nothing fromplugins/wp-erp-pro/in debug.log11333.333…)fputcsv()without$escape)WP_DEBUG_LOG, export employees/CSV: no deprecation indebug.log; file unchanged. Still runs on PHP 7.4-1hit row 1; over-long leave reason 500; balance card total counted refunded days#/employees/newin 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 N2026-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/)#/announcements(read-only list, opens the dialog, marks read)#/reportsAction visibility (both apps)
Server fixes to regress
GET /erp/v1/hrm/leaves/requestsas employee/leaves/policiesreturns real namesCommits: free
72d9e603c,9cbcd6f69,be3f06125,e8fe9ce81,58670dd09; Pro6c21227a,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
erp/v1REST moved to Pro. Free registers onlyerp/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).1. The React bundles are not in git (now 11 pro bundles, the HR Frontend portal included).
assets/dist-reactis ignored in both repos. The release build (bin/build.sh, which runsbin/build-react.sh:@erp/hr-sharedfirst, 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.0and pro1.7.0; the release is 2.0.0 for both. At release: free header,WPERP_VERSION,package.json;WPERP_HRM_MIN_VERSIONin free and pro; pro header andpackage.json(the zip name comes frompackage.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
developand are up to date with it as of 2026-10-06 (0 commits behind). Re-sync before merge ifdevelopmoves.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:
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 oldphpcs.ymljob (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-redesignin 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 againstwp-erp/erp-prounless a repo is named.Related issues
Free HRM React admin redesign. Feature issues (free + pro) are logged on the pro repo
wp-erp/erp-proby 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 (
Å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:
requiredaborted submit before the React summary rannoValidateon the form; one validator owns the reportingshared/utils/dialog.ts→dismissGuard(), applied to 5 dialogs0pay_rate0 untouched)closeAfterSave()→/employees/:id; Cancel keeps history-backRestControllerV2::cast_calendar_day()— send the calendar day, not an instantcreated_at/updated_atstored2026%dinerp_settings_save_leave_years()skipped; the page says so and keeps the dialog openjob_title/work_location— the accessors that exist (legacy view fixed too)Åland Islands#/dashboard404'dshared/hooks/useListUrlSync.tsTest specification and per-area case files live in
QA_manual_test/— surface derived from theroute 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