The update from the legacy and very outdated PHP-based WordPress table to modern React based interface design has happened. This is the first iteration with other changes planned in the coming months. Here are some of the updates including some changes from the July release that are already online.
Retooled Quick Search
The revised quick search feature.Searching across all fields for the 2059 text (found it in the latitude field in this example).
We’ve completely retooled the quick search feature.
It still searches across multiple fields, but it now searches across all text fields in the location set not only the name and primary address fields. And it is fast. Much faster than past iterations.
Filter Locations
Complex location filters coming in the August 2026 release of SLP.
Complex filters can be built on locations. All of the filters we had with the previous filters drop down menu are still available, plus dozens of additional options.
The filters apply across the entire data set with response times that are vastly improved over the prior versions. Large sets of locations can now be filtered with ease.
Built a wide variety of logic combinations for any location data field.
Data matching options include:
contains | does not contain
equals | does not equal
starts with
ends with
is empty | is not empty
is any of
All fields can be combined with AND or OR logic to display specific locations.
Change The Column Layout
A menu of new column layouts.The name column pinned to the left, the other data scrolls under it.
A myriad of options are now available for how you see your data on the locations list.
Sort columns in any combination.
Drag columns to reorder them.
Pin columns to the left or right of the table.
Hide columns.
The best part is Store Locator Plus® remembers your customization between sessions. No more re-sorting or changing column layouts every time you log back in or visit a different page. We’ve added a notification with an easy reset button in case you ever need to quickly get back to the default layout; Something we’ve found useful when we can’t remember which columns we’ve hidden.
Change The Row Density
Row density options.
While more row management options (like grouping, ordering, etc.) are coming – for now we’ve given the basic option to set the height of the display with several row density options available: Compact, Standard, and Comfortable.
Goal: replace the legacy locations management table written in PHP and jQuery with a modern React component preferably MUI DataGridPro.
Legacy Locations Management Table
Rendering The UI
Rendering of the PHP page in WordPress is routed via this path: – WordPress admin menu “Locations” (WordPress plugin) or “Location Details” (SaaS) for menu_items[‘slp_manage_locations’] – Admin Menu calls \SLP_Admin_UI::renderLocationsPage to render the page. – Calls \SLP_Admin_Locations::display
This renders the location manage page which has several tab panels. Each tab panel is added or augmented with the slp_build_locations_panels custom WordPress hook. The Locations Management Table is added with a hook priority of 10 that calls \SLP_Admin_Locations::addListLocationsSetting
When the page renders it goes inside an HTML div (tab panel) that is exposed when a user clicks on the List menu item in the horizontal tab menu that is currently rendered as a React component on the same page.
The List Locations Tab Panel Content
This page is comprised of HTML from the following methods:
\SLP_Admin_Locations::create_string_location_table_start – starting HTML wrappers for the locations table
\SLP_Notifications_Manager::get_html – renders a notification or “SnackBar” style notices manager if any notification messages are on the notifications array.
Executes the location query via \SLP_Data::get_SQL
Gets a total location count and stores it in \SLP_Admin_Locations::$total_locations_shown
Adjust the pagination starting page number if necessary storing the value in \SLP_Admin_Locations::$start
If no locations, adds a WordPress nonce with wp_nonce_field( ‘screen-options-nonce’, ‘screenoptionnonce’, false ) and shows the no locations message and returns.
If there are locations:
Outputs a hidden input field name=’start’ with the starting offset for pagination
If the page is rendered after a form post (processing the apply to all button) outputs a hidden input name=”apply_to_all” with a value=”1″
Task : Replace The Manage Locations PHP and jQuery managed table with MUI DataGrid Pro
Licensing MUI DataGrid Pro
There is a license for MUI DataGrid Pro. You can find an example in the HistoryTable React component in MySLP Dashboard (wp-content/plugins/myslp-dashboard/src/settings_history/HistoryTable.tsx). The MySLP Dashboard plugin invokes the license in that component via import “@licensing/MuiLicense.js”; This methodology can be copied for SLP.
Guidelines
Use the built-in DataGrid features for sorting, filtering, checkboxes to select row, pagination, and other extended features of the MUI component.
Extract the data manipulation elements from the legacy Store Locator Plus location management PHP code and integrate them into a location REST endpoint in the Store Locator Plus plugin.
The Store Locator Plus plugin already has REST endpoints for fetching location data
These existing endpoints may need to be augmented to accommodate all features of the manage locations table
Be sure to follow any custom WordPress hooks and filters employed to manipulate data
Pay special attention to hook and filters in the Experience, Power, and Premier plugins
Connect the interactions of the React LocationsTable component that was started in wp-content/plugins/store-locator-plus/src/LocationsTable/LocationsTable.tsx to the data display of the new MUI DataGrid Pro table for locations.
The LocationsTableHeader React component has been designed to interact with this data display table.
This component currently interacts with the legacy PHP and backend data processing interfaces.
Update the interactions as appropriate to take advantage of the new MUI Data Grid Pro features.
Locate the local AI knowledge and context files related to prior work on the location management interface, especially task summaries related to the LocationsTableHeader components.
If unsure of any step in the process, pause and ask for guidance.
If any step of the process is not resolving in several turns, pause to explain the issue and proposed path forward and ask for guidance or assistance if needed.
Feedback Turn 1 : Store Locator Plus plugin version 2607.22.02
Tasks
✅ Revert the action bar styling , see details below.
The per-row checkbox does not work, clicking the checkbox does not change the state of the checkbox UI on the row.
Pagination does not work. Reproduction:
Set page length to 10. Page 1-20 of 21 shown.
Click the > to move to the next page. Page redraws showing 1-10 again.
✅ Fix Bulk Actions Categorize Slide Out Styling , see details below. Style is not correct.
✅ The edit button on each row (in LocationsDataGrid) does not invoke an action.
✅ Fix the add tags modal styling, see details below.
✅ Drop the With These Properties option from the filter drop down (LocationsFilter)
Remove associated React code to show the slide out drawer and render the selectors.
✅ Remove the LocationsSearch component from the LocationsTableHeader action bar.
Remove associated React code for display as well as related processing that is not in use by the DataGrid Pro implementation.
Revert The Action Bar Styling
The Manage Locations action bar (LocationsTableHeader component) style was changed. The image below was what it looked like before the Codex GPT-5.6-Sol agent created the React version of the manage locations table. This styling should have not been changed.
Version 2607.22.01 – original manage locations action bar styling.
The revised action bar is shown here:
Version 2607.22.02 buttons colors changed.
Color scheme of apply, apply to all, and search buttons have changed.
Bulk Actions Categorize Slide Out Styling
The apply and apply to all are buttons not the top right as icons next to the close button.
The categories are following the WordPress hierarchy display
Version 2607.22.01 – original categorize panel stylingVersion 2607.22.02 – Categories not hierarchical , button colors changed
Fix the add tags modal styling
v26072.22.01 – original stylingv2607.22.02 styling
For Review
With These Properties Filter Not Rendering
THIS IS NOT AN ACTIONABLE ITEM. This is a record of the changes made by the AI agent that produced a subpar result.
The “With These Properties” filter can be removed as MUI DataGrid Prod provides more advanced per-column filtering.
The drop down filter menu (rendered via the LocationsFilter component) no longer shows the slide out filter properties selector.
Select “With These Properties”
This should show a slide out drawer where a user can pick the properties to filter on (reference first image below), the drawer is not rendering on the visible UI surface (reference second image below).
v26072.22.01 – The With The Properties slide out UI is rendering.Version 2607.22.02 – With These Properties slide out component is not rendering
Feedback Turn 2 : Store Locator Plus plugin version 2607.22.03
Tasks (Issues)
Click the per-row checkbox does not check the box.
Cannot select single rows in the DataGrid.
✅ Pagination does not work.
Reproduction:
There are 25 locations
Set page length to 10. Page 1-20 of 21 shown.
Click the > to move to the next page. Page redraws showing 1-10 again.
Tasks (Improvements)
✅ Separate the latitude and longitude after the business name into two new separate columns on the data grid.
Keep the name column pure with only the name rendered.
Add the latitude and longitude as separate columns as they are in the database.
This may require a back end change depending on how the REST data is procured.
✅ Remember the state of the DataGrid columns.
The following properties of the DataGridPro should be remembered across page loads or table interactions:
Column Order
Hidden Columns
Filters
Sort Order
The state should be stored in the browser React state between page refreshes.
The state should make a REST call to store this state in the WordPress user meta as well.
This will likely require a new REST endpoint.
The meta key should start with the SLPLUS_PREFIX plus a name you deem appropriate (maybe csl-slplus-LocationsGridState)?
Use that user meta to remember the table state if the user moves off the page to another interface in WordPress and comes back.
This will also allow the table to remember the state if a user logs off and comes back the next day.
✅ With DataGridPro there is an option to lock the table header (column names) and footer (pagination) and vertically scroll rows in between.
The display mode also appears to be able to match the height and width of the DataGridPro to the bounding box.
Feedback Turn 3 : Store Locator Plus plugin version 2607.23.01
Tasks (Issues)
✅ Click the per-row checkbox does not check the box.
This issue has been a problem since Turn 1 and is going to need human intervention.
Cannot select single rows in the DataGrid.
The check/uncheck a single location does not work.
Tasks (Improvements)
✅ Remove the LocationsFilter component from the LocationsTableHeader component.
The last remaining filter “Show Uncoded” is no longer needed now that latitude and longitude are on the column list.
Move the latitude and longitude column to be directly after the distance column on initial startup state.
✅ When the DataGridPro meta data about column order, hide/show, or filters are in place add notification box above the table to let the user know “Custom table settings are in place. Some columns may be hidden or reordered.”
Feedback Turn 4 : Store Locator Plus plugin version 2607.23.02
Current (left) and preferred (right) notification box location.
Tasks (Improvements)
✅ Move the notification box about custom columns to the top right.
✅ Introduce a “Reset” button in the “Custom table settings” notification.
When the reset button is clicked, reset the DataGrid state back to the original defaults updating the stored state and related user meta in the process.
✅ Add the built-in GridToolbarFilterButton and GridToolbarDensitySelector to the DataGridPro component in LocationsDataGrid.
✅ Add an alternating row background to the data rows in the DataGridPro rendering.
✅ The initial height for the DataGridPro component appears to be set according to the size of the slp-locations-grid or .react-wrapper div.
This element includes the LocationsTableHeader component and LocationsDataGrid component.
The height should be set by the available space in the outermost bounding layout element in LocationsDataGrid.
Observations
Actions to not need to be taken here. These are findings while reviewing the AI agent work.
To deal with styling issues with the React components, the AI Agent (Codex 5.6-Sol) added <div className=”react-wrapper”> to the LocationsTable component.
This would be more efficient by adding class=”react-wrapper” to the \SLP_Settings_manage_locations_table::display method that builds the div this React component attaches to.
Why? It creates one less HTML element the DOM processors need to navigate during processing.
Imports for React components are using “barrel imports” versus names imports.
This is less efficient in development environments.
\SLP_Admin_Locations::createstring_FiltersBlock creates the HTML string to render on the locationForm. This fires the custom WordPress filter slp_locations_manage_filters to build an object array of properties for the drop down menu. It builds up the $baExtras string if any object in the array has an ‘extras’ as a property. It uses the $baExtras to build a dialog box modal attached to the #locationForm HTML Form on the page to add extra properties to form submissions. This dialog is shown when specific drop down elements are selected.
This filter is only used by the Power add on to extend the filter array of objects noted above. The method that extends the array is \SLP_Power_Admin::filter_LocationsFilters.
Two filter drop down entries create extended modal dialog box interfaces:
The modal dialog content when “With These Properties” (filter_by_property) is picked on the filter drop down.
Development
Follow the same design principles behind the Bulk Actions rewrite to make a React-based component for the location filters interface.
Create a new LocationsFilter component that is a sibling to the LocationsSearch and LocationsBulkActions (wp-content/plugins/store-locator-plus/src/components/locations/LocationsBulkActions.tsx) components. Use a style similar to that for bulk actions. Do not add an apply and apply to all button, instead use a filter icon button to submit the drop down selection.
Instead of a dialog box, use the same style slide out drawer used for Bulk Actions “categorize”. Slide out from the right side, attached to the same parent div as the Drawer for the categoryDrawer. Create the input elements in the same slide drawer as the categories filter following the order:
Name : input box
Zip: input box
State : make this an accordion that is collapsed by default
Inside the accordion use a checkbox list for all the states, built form the database of locations
Country : make this an accordion that is collapsed by default
Inside the accordion use a checkbox list for all the countries, built form the database of locations
Category : make this an accordion that is collapsed by default
Use a checklist of categories similar to that created for the BulkActions categorize interface
Retain the legacy jQuery driven form submission process to submit and process these filters. Use styling similar to the Bulk Actions categorize drawer. Create new REST endpoints only if necessary to fetch a list of states or countries from the list of locations.
Remove any legacy code that has been replaced by the new React interface after validating functionality.
Goal: At the top of the Locations | List interface there is a search box for locations. This is currently rendered and managed with PHP+HTML+jQuery. Add this to the new React LocationsTableHeader component and deprecate the legacy code.
Research
Current UI/UX
This image shows the new Bulk Actions React component with the legacy filter and search PHP+HTML+jQuery UI below.
HTML output string is generated in \SLP_Admin_Locations::createstring_SearchBlock. This uses HTML based onkeypress and onClick attributes to trigger JavaScript actions. Pressing enter in the search box or clicking on the search icon runs the jQuery-driven AdminUI.doAction(‘search’)
Development
Create A New LocationsSearch React Component
Create a new LocationsSearch React component that is a sibling of the LocationsBulkActions React component. Create a search input box with a search icon button after that triggers the search.
UI/UX Updates
In LocationsTableHeader use MUI components to wrap all children that will allow for horizontal stacking of children.
LocationsTableHeader should take 100% of the width of parent .react-wrapper div.
If the children don’t fit
wrap the entire child component to the next line
do not use a horizontal scroll bar
Place the new LocationsSearch React component to the right of the LocationsBulkActions component.
All of for a future LocationsFilter component to be placed between the LocationsBulkActions and LocationsSearch components.
The selection on the bulk actions menu is executed via a jQuery on ‘click’ action that is attached to two divs posing as buttons.
The button interactions are driven via jQuery hooks that live in wp-content/plugins/store-locator-plus/js/admin-locations-tab.js The invocation hooks via jQuery on ‘click’ and the methods that are invoked are in the SLP_Locations_table_header “class” in admin-locations-tab.js.
Apply
Intended to run the action against the locations that have been checked off using the locations table checkboxes which is rendered with PHP and HTML.
DIV ID #do_action_apply
Apply To All
Intended to run against ALL locations in the database.
DIV ID #do_action_apply_to_all
Render Additional Metadata User Interfaces
Some of the items on the drop down menu allow for extra meta data to be set by the user. This meta data is sent along with the other form data for the locations table to the backend when the Apply or Apply To All buttons are processed via jQuery.
The only two use cases are for:
User category selection
attached to the ‘categorize’ dropdown item
shows the div with a checkbox list of categories available to locations when the users selects this dropdown item
the categories come from the WordPress taxonomy system using the SLPlus::locationTaxonomy (set to ‘stores’) property to determine the taxonomy label
User tag input
attached to the ‘add_tag’ dropdown item
shows a div with an input text box where the user can enter a string of comma separated values
The SLP_Admin_Locations class leverages multiple methods from the SLP_Settings class via \SLP_Admin_Locations::$settings to manage the current PHP/HTML/JavaScript heavy implementation. The $settings property and thus SLP_Settings class manages much of the PHP-to-React interfaces.
A primary method of “feeding” variables from WordPress, PHP, and the underlying SQL data is managed via the \SLP_Settings::get_vars_for_react method
Development
Pre-Existing Issue
With the Export, Hosted CSV bulk action and checking the first 5 items, the export worked but the “Location Processing Info” box with the download link cannot be closed from the UI.
This should close when clicking outside the box.
Consider changing the header in the confirmation modal to the action name, in this case “Export, Hosted CSV”.
Second Turn Review
UI/UX Issues
LocationsTableHeader component needs some left margin/padding to align with the legacy PHP-derived table output below. It should not be flush against the left sidebar menu interface.
The Apply / Apply To All / Close buttons on the revised Category slide out drawer look awful and needs to follow modern design best practices.
Redesign the header of the slide out to follow a design like this:
A clear header box (white on white) with the text “Categorize Locations” instead of “Categories” in place of Settings in this example.
Use simple icons from MUI Icons with highlighted tool tips on hover (immediate, no wait)
CloseOutlinedIcon for close
ChecklistOutlinedIcon for Apply
FactCheckOutlinedIcon for Apply To All
In addition, I see the LocationsBulkActions component is using a deprecated property in the Drawer component.
PaperProps is deprecated for MUI <Drawer…>
On the Tag, Add modal add the Apply and Apply To All buttons
Change “done” to cancel.
Follow the same implementation as the category slide out, fire the underlying “apply” and “apply to all” functions from the main bulk actions form.
Apply , Apply To All, and Cancel should all be action buttons on the bottom of the modal.
When this modal exits, reset the Bulk Actions drop down back to the default no action “Bulk Actions” selection (first selection) same as when the categorize slide out closes.
Initial Turn Review
UI/UX Issues
Do not need “Bulk Actions” label around the drop down selector AND the word “Bulk Actions” as the first entry in the drop-down menu.
If the Bulk Actions in the border around the selector is considered best practices for a Material UI interface, leave that one remove the “Bulk Actions” from the first entry in the drop down menu, otherwise remove the border and “Bulk Actions” label entirely.
The box containing the <LocationsBulkActions/> component needs some padding above it to provide visual separation from the AdminHeader page title and tab bar (horizontal menu).
Sort the drop down list of bulk actions alphabetically.
Change the text from “Stop Featuring Location” to “Feature Location, Stop”
Change the text from “Feature Location” to “Feature Location, Start”
Change the text from “Tag” to “Tag, Add”
When choosing the Categorize bulk action, the side drawer does not render in the div#wpbody HTML element, causing the top portion to be obscured by the div#wpadminbar generated by WordPress.
When closing the Categorize slide-out the drop down menu should re-select the first entry
The issue is after closing categorize the user will need to select a different drop down entry to be able to show categorize again, this creates extra steps to re-draw the categorize slide out.
Add another pair of buttons to the top of the categorize slide-out for:
apply – does the same thing as the bulk action “apply” button
apply to all – does the same thing as the bulk action “apply to all” button
close the slide out after either slide out button or the slide out close icon is clicked
The extra meta input for the add_tags drop down entry has a label “comma separated tags” that is hard to read due to the border outline.
When going to other tabs on the Location page such as Add, Import, or Load, the new LocationsBulkActions component should be hidden, it only applies to the List tab.
Eventually the LocationsBulkActions will be within a TabPanel MUI React component driven by the tabs alongside the actual list of locations data table (currently rendered with PHP) and will be managed by the MUI tabs interface.
As such it may be prudent to wire this as a standard MUI TabPanel instead of inside a generic Box component and let the AdminHeader sections perform the standard tab-switching built into MUI.
Code Review
\SLP_Settings_manage_locations_table
In \SLP_Settings_manage_locations_table::get_bulk_actions_for_react the filter slp_locations_manage_bulkactions is applied. One of the filters calls \SLP_Power_Admin_Locations::extend_bulk_actions. Some of the entries in the returned array from \SLP_Power_Admin_Locations::extend_bulk_actions includes a lot of HTML stored in the ‘extra’ property of some of the array elements (see ‘add_tag’ and ‘categorize’ in \SLP_Power_Admin_Locations::extend_bulk_actions). The values in the array returned by the filter is then passed through \SLP_Settings_manage_locations_table::normalize_bulk_action_for_react which replaces any ‘extra’ properties with a simple string of ‘tag’ or ‘categories’. This makes all of the information stored in the ‘extra’ properties defined in \SLP_Power_Admin_Locations::extend_bulk_actions unnecessary. I have removed the excess overhead from \SLP_Power_Admin_Locations::extend_bulk_actions. This should have been caught in the code review process. Creating solutions is great. Leaving behind a mess of unused legacy code that is not longer useful is not great.
$baExtras is built from The List Of Dropdown Options that was extended via the slp_locations_manage_bulkactions filter.
LocationsTableHeader React Component
TypeScript source: wp-content/plugins/store-locator-plus/src/components/locations/LocationsTableHeader.tsx Part of the store-locator-plus plugin. New as of Store Locator Plus v2606.30.01
This is where the Bulk Actions will end up being rendered when this task is finished. Eventually we will add the location filters and search interfaces to the LocationsTableHeader component.
For this task I suggest creating a new component alongside (in the same directory as) the LocationsTableHeader React component named LocationsBulkActions. Render that in place of the existing “<p>Locations Table Header</p>” placeholder in the LocationsTableHeader component.
Setting Up The Dropdown List
Create a local get_vars_for_react method in SLP_Admin_Locations that extends the \SLP_Settings::get_vars_for_react method attached to the SLP_Admin_Locations\settings property.
It should store the bulk actions dropdown options in an array property that is added to the existing var being managed by the get_vars_for_react parent methods. When it reaches this new method in SLP_Admin_Locations\get_vars_for_react, which should call the $this->settings->get_vars_for_react() method first, the general properties available in the array should be:
Setting Up The Additional Metadata User Interfaces
For this element we are dealing with two fairly static components, a category checklist for the ‘categorize’ dropdown option and a text input for tags for the ‘add_tag’ dropdown option.
add_tag additional metadata interface
Since the underlying location tag data properties are always available, there is no need to only render this interface when the Power plugin is active. As such this can be directly added as a modal interface in LocationsBulkActions. The interface should only be shown when the ‘add_tag’ dropdown option is selected.
categorize additional metadata interface
This component should only be shown when the ‘categorize’ drop down is selected.
The list of category checkboxes may be better served being shown in a slide-out drawer attached to the right side of the page.
The context should be a checklist of the available categories from the WordPress taxonomy system for the \SLPlus::locationTaxonomy (‘stores’) taxonomy. The checklist should honor the hierarchy system of the category list, rendering children indented one level directly underneath their parent entry.
I suggest Axios and a REST endpoint to fetch the category list the first time the ‘categorize’ drop down option is invoked. Store the response in a state variable to prevent future REST queries during a single user interaction. Show a loading indicator while fetching the list of categories.
The Datatables JavaScript library is outdated based on a legacy jQuery oriented approach. The goal is to replace DataTables.js with the licensed MUI DataGridPro interface.
With the current version of Store Locator Plus® the DataTables jQuery interface was unused. It used to be part of the location table list interface to try to modernize the WordPress list tables.
Goal: Create a persistent accounting information data store using the built-in WordPress Multisite database surfaces.
The table should follow some standard rules that will allow us to implement CRUD operations via REST endpoints in the MySLP Dashboard plugin.
Tasks
One time task “Create Or Update The Database” when the MySLP Dashboard version indicates a change.
Create a Monthly Ledger User Interface to allow for manual CRUD operations on the slp_accounting_monthly_history table.
Monthly History Data : Environment
Environment
All accounting history data will reside in tables for the main WordPress super admin account.
The Main WordPress Super Admin account site ID is stored in the PHP constant SITE_ID_CURRENT_SITE and should be 1. The database table prefix should come up as wp_ unlike user accounts that start with wp_<user_site_id>.
slp_accounting_monthly_history Table
The table should be named $wpdb->prefix . “slp_accounting_monthly_history” resulting in a table name of wp_slp_accounting_history.
slp_accounting_monthly_history Structure
ID – a unique ID to allow for explicit record I/O for CRUD operations.
KEY – a short string indicating the type of monthly data for example:
“SAAS_REVENUE”
“PLUGIN_REVENUE”
AMOUNT -an integer representing the numeric value for the key for example:
3500 which may represent “35.00” or 35 dollars in our UI surfaces
DATE – a date and time for they entry representing the date this value represents NOT when the data was created or modified
This Accounting Monthly History Data interface will run various monthly cron jobs to collect data and store it in this table. We will use this data to product things like monthly revenue graphs for the SaaS Account Overview dashboard. The graph will show SaaS Revenue from active accounts on a monthly basis.
We will do the same providing a manual process to add other revenue such as WordPress plugin revenue under “PLUGIN_REVENUE” keys.
We will allow for users to change the Line Chart revenue graph to show “monthly” or “yearly” graphs. Future data keys may warrant daily graphs.
Task: Create Or Update The Database
When the MySLP Dashboard module version (plugin version) changes, run a database “create or update” hook to create the table or modify it as needed.
Copy the design pattern in the Store Locator Plus plugin in the \SLP_Admin_Activation class. wp-content/plugins/store-locator-plus/include/module/admin/SLP_Admin_Activation.php This is fired from \SLPlus::initialize_after_plugins_loaded When the Store Locator Plus version reported is newer than the installed version for the plugin (see version_compare( $this->installed_version, SLPLUS_VERSION, ‘<‘ )) , the MySLP Dashboard should follow that pattern.
Task: Monthly Ledger User Interface
Admin Menu
Add a submenu with the label “Monthly Ledger” under the “Accounting” admin sidebar menu we created in \MySLP::create_network_admin_menu.
Add a new My MySLP_Account_MonthlyLedger PHP React loader and helper class. Follow the design pattern of \MySLP_Accounting in wp-content/plugins/myslp-dashboard/include/accounting/MySLP_Accounting.php Place it in the same directory as MySLP_Accounting.php as a sibling.
Create a new React component that allows basic CRUD operations on the slp_accounting_monthly_history Table. Attach it to the MySLP_Account_MonthlyLedger PHP React loader. Follow the UI design pattern of the AccountingPanel React component at wp-content/plugins/myslp-dashboard/src/accounting/accounting.tsx Use a MUI X DataGridPro component as the primary table interface. Allow for pagination, starting with a default page length of 25 records. Sort the records by DATE descending on initial load so we see newer records at the top. Provide an add record interface where a user can enter the KEY, AMOUNT, DATE. Provide an inline edit record interface on the DataGridPro table to allow users to click on they KEY, AMOUNT, or DATE field on an existing record and change it. Provide a delete option for each record.
Data Interface
Use REST for all CRUD operations and to fetch the initial data set.
When records are added the backend data interface should:
Ensure all keys entered are trimmed (no leading/trailing spaces) and are shifted to uppercase.
AMOUNT is stored only as an integer.
DATE field needs to allow for flexible input converting text input into a best guess for the date to be stored as a date-time entry in the database. Examples:
“05/2026” is May 2026 which should be recorded as the date time 2026-05-01 00:00:00
“05/01/2026” is May 1st 2026 which should be recorded as the date time 2026-05-01 00:00:00
“May 2026” is May 2026 which should be recorded as the date time 2026-05-01 00:00:00
Allow for various separators such as / or – or .
05-01-2026 is the same as 05/01/2026
05.01.2026 is the same as 05/01/2026
Use standard Date/Time JavaScript or React libraries for data manipulation.
The manipulation can be not the front-end (React/JavaScript) before communicating with the REST endpoint.
The backend should do basic sanitation before recording data.
The “Buffer” For Overages
Legacy billing was sometimes strict on the overage policy. The revised automated report, not so much as there is a 100 views “free buffer” on overages.
2026 : Jan | Feb | Mar | April | May
Acct ID
Spreadsheet
New50
BRmarket
0 – 0 – 15 – 0 – 20
0 – 0 – 15 – 0 – ?
MKbevis
0 – 0 – 5 – 0 – 0
0 – 0 – 0 – 5 – 5
TKESeck
0 – 0 – 0 – 5 – 0
0 – 0 – 0 – 0 – 0
Arbor
0 – 0 – 0 – 0 – 35
0 – 0 – 0 – 0 – 35
UWS
0 – 20 – 25 – 45 – 115
15 – 20 – 25 – 45 – 115
Gary
10 – 15 – 20 – 25 – 25
0 – 15 – 20 – 20 – 25
The actual billing and monthly views do not always match.
Part of this is the monthly view total is triggered when the Stripe billing renews. When a Stripe renewal is successful the SLP SaaS platform records the current view count to a permanent record of views for the previous customer billing period (prior month). The system is imperfect and need refinement as Stripe sometimes needs to reprocess subscription renewals for a myriad of reasons. As such the view tally is sometimes recorded more than once and reset more than once. This impacts the total view count recorded for the month as well as the billing.
Starting in June 2026, we are using the new monthly views report as the source of truth. This may not be perfect during the period we work to resolve these corner cases with Stripe renewals. The map views will never be OVER actual usage. Worst case, we bill a little LESS due to this issue.
Overages remain billed at $5 for a block of 1,000 extra views. As an unofficial policy while we work toward resolution, all accounts now have a “100 free overage views” allowed on their account. Thus, if an account is over views we don’t charge for the block until the new block gets 101+ views. Examples: limit is 5,000 – actual views is 5099 – billing is 0 extra blocks as 99 is under the 100 grace = $0 limit is 5,000 – actual views is 5250 – billing is 1 extra block at 250 exceeds the 100 grace = $5
limit is 5,000 – actual views is 8099 – billing is 3 extra block as 99 is under the 100 grace = $15
limit is 5,000 – actual views is 8250 – billing is 4 extra blocks = $20 (1 extra block: up to 6,000 views, 2: up to 7,000 views, 3: up to 8,000 views, 4: up to 9,000 views) = $20
As a result, months prior to June 2026 may not align with billing and view count.
We are creating a full Store Locator Plus accounting dashboard to track both the SaaS platform as well as WordPress plugin sales.
Sales Data
The primary source of truth for sales data will come from Stripe. Any references to PayPal payments or accounts can be considered legacy information and can be left out of the accounting panel.
The payment module holds the Stripe API module and related code for payment system communications.
User Interface
Stage 1 : Accounting Overview Scaffolding
Accounting Sidebar Menu
A new sidebar menu should be created in WordPress for super admin users only. \MySLP::create_network_admin_menu should be used to create and attach the menu. Follow the design pattern for the configure menu option, code snippet follows:
Unlike the main configure menu page that is rendered via \MySLP::render_configuration_page the new accounting module should load and render a React PHP helper class. We want this interface to be primarily React driven.
The WordPress PHP React Helper Class
Follow the \MySLP_Customer_Profile class for guidance on implementing a React user interface within WordPress. I suggest creating a new class MySLP_Accounting in wp-content/plugins/myslp-dashboard/include/accounting.
The React AccountingPanel Component
The React components that \MySLP_Accounting loads with the help of the wp-scripts node package should live in wp-content/plugins/myslp-dashboard/src/accounting. This directory should have a block.json and an accounting.tsx file.
block.json should probably look like this:
{
"script": "file:profile.js"
}
The accounting.tsx TypeScript should contain the React accounting component. This new React component should be named AccountingPanel. It should follow the UX design pattern of the ProfilePanel component in wp-content/plugins/myslp-dashboard/src/profile/profile.tsx
The initial user interface will only have a single submenu (SLPTabsBar Tabs components) for the main page which should be labelled “Overview” which is already selected and open. It will render the AccountingOverviewPanel component noted below.
AccountingOverviewPanel Component
A supporting AccountingOverviewPanel React component should be created. If the WordPress wp-scripts supports it, this can go in the same directory as the AccountingPanel component : if not place it in wp-content/plugins/myslp-dashboard/src/accounting_overview
This component should be a React Dashboard that shows overall accounting metrics. To start with we will want to show the total number current active accounts and total amount of payments from those active accounts. The ManageCustomers React component (wp-content/plugins/myslp-dashboard/src/manage_customers/manage_customers.tsx) displays this data in the AdminHeader children via a pair of Chips. Use the same REST endpoints to fetch this data and display it.
Here is a screen shot of the Material UI Dashboard Template that is referenced.
For now we need the cards for:
Accounts in place of Users with the active count below. We will add a data table for historical trends and a graph below it in the future. Clicking on the user count in the account card should redirect the UX to load the Manage Customers page at /wp-admin/network/admin.php?page=customer_list
Payments in place of Conversions with the total payment amount below. This too will eventually have trends stored in a data table that we will create later.
AI Summary
Junie Pro Summary
Completed setup and implementation prep for the accounting dashboard work in myslp-dashboard, including running required start-revision on branch accounting.
• Implemented Stage 1 accounting UI scaffold: new Accounting admin page, React entrypoint, and overview metrics/cards with click-through to Manage Customers.
• Added new files: include/accounting/MySLP_Accounting.php, src/accounting/accounting.tsx, and src/accounting/block.json.
• Updated integration points in include/MySLP.php and constants/version metadata in myslp-dashboard.php and package.json.
• Validation status: lint checks for new files passed (only a non-blocking TS import style warning noted earlier), diff checks passed, and you confirmed npm install + npm run build succeeded locally with generated build/accounting/accounting.js and build/accounting/accounting.asset.php present.
• Environment note: Junie shell still lacks local php tooling and direct GitHub auth; you requested follow-up guidance on PAT-based auth after task completion.
Junie Release Notes • Scope: No new product-code changes were made after the previously summarized accounting dashboard work in myslp-dashboard. • Skills/Environment updates this session: Reloaded skills via Vendors/JetBrains/relink_skills.sh; available skills now include /wordpress-release-notes. • Code impact since last implementation summary: None. • Build/Test status: No additional build or test runs were performed after the earlier confirmed successful npm install and npm run build for myslp-dashboard. Included Previously Completed Work (for continuity) • Added Accounting admin page scaffold and React entry (include/accounting/MySLP_Accounting.php, src/accounting/accounting.tsx, src/accounting/block.json). • Wired menu/render integration in include/MySLP.php and version bump artifacts from start-revision (myslp-dashboard.php, package.json).
The Settings Interface architecture drives most of the legacy Store Locator Plus admin interfaces. These are the interfaces customers interact with most often, especially on the SaaS platform. It is the main component of the SLP Dashboard for SaaS.
It was originally written to manage the UI surfaces that present the myriad of settings (options) available to customers. It presents the “dials the users turn” to change how their store locator maps and directory presentations are configured. It grew to cover other admin UI interfaces including the Locations Interface.
Outputs the HTML for settings pages, including the location details page (it uses the “settings” page construct). The output all ends up in a HTML element: <div class=’dashboard-wrapper’>…</div>.
General layout:
<div class=’dashboard-wrapper’>
<header id=”dashboard-header” class=”dashboard-header”> = React blue bar header
// -- include the asset file to get the WordPress Scripts defined dependencies and version ID
$asset = include SLPLUS_PLUGINDIR . 'build/slp_adminheader/script.asset.php';
wp_enqueue_script( 'slp_adminheader', SLPLUS_PLUGINURL . '/build/slp_adminheader/script.js', $asset['dependencies'], $asset['version'], true );
wp_add_inline_script( 'slp_adminheader', 'const slpReact = ' . wp_json_encode( $this->get_vars_for_react() ) . ';', 'before' );
Rendered via the React component at wp-content/plugins/store-locator-plus/src/components/AdminHeader.js
In the current version the header element DOES NOT contain the sub navbar.
It is rendered via SLP_Settings::sublevel_navbar deep inside the <div class=’dashboard-wrapper’>…</div> element.
React Update To Render Subnavbar
The goal is to render the \SLP_Settings::sublevel_navbar HTML output using React instead of the current PHP and HTML implementation.
The AdminHeader at wp-content/plugins/store-locator-plus/src/components/AdminHeader.js renders the top-of-page header with the page name and some interactive icon buttons.
The task is to add a submenu below the Toolbar that is comprised of the sections currently residing in $this->sections in the \SLP_Settings::sublevel_navbar method. Using the $titleText ($section->name) within. The wp_kses_post function current used to set $titleText is not necessary as the $section->name is already considered "clean".
The best way to get the variables into React so they are accessible in JavaScript is to modify the \SLP_Base_ReactObject::get_vars_for_react method. Instead of attempting to override or extend the base \SLP_Base_ReactObject::get_vars_for_react method, add a filter in \SLP_Settings for slp_react_vars. That will extend the react variable array via this return method on the end of the get_vars_for_react method:
apply_filters( 'slp_react_vars', $defaultVars );
You will find a good reference implementation of the slp_react_vars filter via:
\Customer_Profile_Site_Info::initialize - adds the filter for that class
\Customer_Profile_Site_Info::extendReactVars - extends the available JavaScript variables for React
In our case we are not extending MySLP variables, so the SLP_Settings::extendReactVars method should be more like this:
$vars['SLP']['sections'][] = array(
'name' => $section->name,
'div_id' => ! empty( $section->div_id ) ? $section->div_id : $section->slug,
'link_id' => "wpcsl-option-{$div_id}"
);
This should be set up by looping through $this->sections in a new extendReactVars method in SLP_Settings.
This should create a horizontal navbar that shows and reveals each section by the div id. This may require further refinement as the current system uses outdated JavaScript and jQuery to hide and reveal subsequent divs by using the div IDS as noted in the link_id property. For now we will assume that if React renders the components correctly the pre-existing JavaScript will take over.
Initial Results
Works on locations page. Does not work on Settings page. Spacing is not well defined, needs more space between menu items.
The locations page is a bastardized combination of the SLP_Settings class and WP_List_Table.
The top-of-page header appears to be handled by SLP_Settings. SLP_Settings extends SLP_Base_ReactObject To start rendering the page is calls \SLP_Base_ReactObject::render with these attributes: – $scriptHandle = ‘slp_settings’ – $scriptFilebase = ‘slp_settings’ This methodology ends up render the SLPSettingsPanel React component (wp-content/plugins/store-locator-plus/src/slp_settings/slp_settings.tsx)
That means the page header is rendering using the Reactionary standard <AdminHeader> for the page. Currently SLP_Settings is only leverage the pageName property, but it also supports sections as a property which is the submenu.
With the 2606.26.01 release the Settings system uses the React AdminHeader menu.
\SLP_Settings::render_settings_page
$this->sections may have is_topmenu set, this indicates the top level menus in the app
Locations Card (Defunct?)
This may no longer be used: \SLP_Admin_Locations::create_location_details_box
SLP_Admin_Locations
extends WP_List_Table – this is already going to be a shit show. The WP_List_Table interface is from 1902. Maybe before that. It is awful.
Referenced by:
\MySLP_Admin::do_admin_startup()
Setting the screen property , probably not needed: SLP_Admin_Locations::get_instance()->screen = ‘store-locator-plus_page_slp_manage_locations’;
\SLP_Power_Pages_Admin::extend_location_edit()
Only if Store Pages is active, add an edit section to show an “edit Store Page” link
\SLP_Power_Pages_Admin::set_page_actions()
Add the recreate store page action hyperlink under each location on the table
\SLP_Actions::save_screen_options()
To save the screen options, which is currently only the page (table rows) length.
\SLP_Admin_Locations_Add::
initialize
extended_data_block – for extended data fields
\SLP_Admin_UI::renderLocationsPage
To render the UI
\SLP_Location_LoadFromWP::import
To only report back any URL the person puts in is invalid.
This is called from \SLP_Admin_Locations_Actions::process_actions when the current_action is load_from_wp
This is setup from \SLP_Admin_Locations_Load
\SLP_Settings_manage_locations_table::display
A shim for $this->settings->add_ItemToGroup( array( ‘group_params’ => $group_params, ‘type’ => ‘manage_locations_table’, ) );
SLP_Admin_Locations
Hooks & Filters Fired
slp_locations_manage_bulkactions : creates the list of bulk action dropdown items
SLP_Power_Admin_Locations
SLP_Experience_Admin
SLP_Power_Pages_Admin
slp_locations_manage_filters : The filter location drop down items
SLP_Power_Admin
slp_locations_manage_cssclass : Adds extra CSS classes
SLP_Experience_Admin
slp_column_data : Change the data rendered in a column
slp_manage_locations_actionbar_ui : modify the action bar on top of the table
slp_modify_admin_locations_script_data : modify the script data for the JavaScript on the page
slp_manage_location_columns
pls_manage_location_where
slp_manage_locations_buttons
slp_build_locations_panels
Not used:
slp_invalid_highlight
slp_edit_location_change_extended_data_info
slp_manage_expanded_location_columns
Screen for WP_List_Table
This is messing up the UI. Let’s see if we can drop this, start peeling away layers to simplify this interface as we work toward a React version.
locations_per_page
Tracked via \SLPlus::$userMetaKeys
Stored in user meta via \SLP_AJAX::update_slp_user_meta
\SLP_Actions::init sets a filter on set-screen-option … it calls…
\SLP_Actions::save_screen_options … which only runs if the current page is slp_manage_locations… if so it calls…
\SLP_Admin_Locations::save_screen_options
Which returns the value of locations_per_page
Best option is to make this a React interface for page length / pagination