Visitor Routing

[Currently in closed testing]

=== Visitor Routing ===

Tags: queue, visitor management, kiosk, ticketing, reception
Requires at least: 6.0
Tested up to: 6.5
Requires PHP: 7.4
Stable tag: 2.8.9
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Topic-aware visitor queue management for WordPress: desks, automatic round-robin routing, a drag-and-drop live queue, operator terminals, and a wall display with sound.

== Description ==

Visitor Routing turns a WordPress site into a complete walk-in queue system for receptions, service centres, clinics, and government offices.

Visitors take a ticket (optionally choosing what they need help with). The router automatically sends each ticket to an eligible desk using a fair, least-loaded round-robin so no single desk gets overloaded and topics are never assigned to a desk that cannot handle them. Operators work their queue from any browser, and a wall-mounted display board calls the next visitor with an on-screen flash and an optional sound.

**Core features**

* **Desks** — create as many service desks as you need, each linked to a WordPress user account who operates it.
* **Topics** — define the kinds of help you offer (each with its own colour and ticket prefix). Assign topics to the desks that can handle them, or mark a desk as handling everything.
* **Automatic routing** — at check-in, tickets go to the eligible desk with the lightest load; ties are broken by least-recently-assigned for true round-robin fairness. Tickets with no capable desk wait in an unassigned pool until an admin routes them (override).
* **Live queue board** — a drag-and-drop admin screen to see every desk’s queue in real time, reorder tickets, move a visitor between desks, or bulk-reroute a desk’s whole queue elsewhere.
* **Operator terminal** — each desk opens/pauses itself, calls the next visitor, closes a visitor (removing them from the system), or reassigns them to another desk.
* **Display board** — a full-screen status screen showing each desk’s current number, the last three served numbers, and an optional estimated wait time. When a desk calls a visitor the board flashes and (optionally) plays a sound — a built-in “ping” or a custom audio file you upload.
* **Daily ticket reset** — optional automatic numbering reset each day.

All four views (check-in, operator desk, display board, admin queue) work in real time via lightweight polling, so no extra real-time server is required.

== Installation ==

1. Upload the `visitor-routing` folder to `/wp-content/plugins/`, or install the ZIP via **Plugins → Add New → Upload Plugin**.
2. Activate the plugin through the **Plugins** menu in WordPress.
3. Go to **Visitor Routing** in the admin menu.
4. Create your **Topics** (e.g. “General”, “Payments”, “Appointments”).
5. Create your **Desks**, link each to a WordPress user, and tick which topics it can handle (or “handles everything”).
6. Open **Screens & Links** to get the URLs/shortcodes for the check-in screen, the operator terminals, and the display board.

== Usage ==

You can place each view either as a shortcode inside a page, or open the standalone full-screen kiosk URL (no theme chrome) listed on the **Screens & Links** page.

Shortcodes:

* `[visitor_routing_checkin]` — visitor self check-in.
* `[visitor_routing_desk]` — operator terminal (the logged-in operator sees their own desk).
* `[visitor_routing_display]` — the public display board.

Standalone kiosk URLs use `?vr_view=checkin`, `?vr_view=desk`, or `?vr_view=display`.

**Sound on the display:** browsers block audio until the user interacts with the page, so the display shows a one-time “Tap to enable sound” button. Tap it once after loading the board on the screen device.

== Frequently Asked Questions ==

= Who can operate a desk? =
Operators must be logged in. The plugin adds a dedicated `vr_operator` role with only the capability needed to run a desk, plus the standard `read` capability. Administrators can both manage routing and operate desks. An operator can only access the desk linked to their own user account.

= Is the visitor check-in screen protected? =
The check-in endpoint is intentionally **unauthenticated** — visitors should not need a WordPress account to take a ticket. It is protected by per-IP rate limiting (max 5 check-ins per 30 seconds) and strict input validation. If you expose check-in on a fully public, unattended kiosk, consider adding a CAPTCHA or additional network-level rate limiting in front of it.

= Does it need a separate real-time server? =
No. The views poll the WordPress REST API on a configurable interval (default 4 seconds).

= What happens to closed visitors? =
Closing a visitor marks the ticket done and removes them from all queues. They are kept only for the “recently served” display and wait-time statistics.

== Security ==

Security was a design priority:

* **Capability-gated REST API** — every privileged endpoint checks a specific capability. For logged-in users, WordPress’s cookie authentication requires a valid `X-WP-Nonce`, which provides CSRF protection. Operators are restricted to their own desk.
* **Admin forms** — every admin action verifies a nonce (`check_admin_referer`) and the managing capability before running.
* **Public check-in** — unauthenticated by necessity, but rate-limited per IP and fully validated/sanitised.
* **Database** — all queries use `$wpdb->prepare()`; all output is escaped.
* **Custom role** — operators receive the minimum capabilities required, not broad admin access.

== Changelog ==

= 2.8.9 =
* New first-run setup wizard: a fresh install now greets the administrator with a guided setup that creates a location, a few example topics, and appoints a check-in desk operator and a desk operator (with a Desk 1 ready to use) — all in one screen, so a new install ends with a working, understandable system. The wizard is also offered again after a full reset, and can be skipped.
* Generic naming: the visitor identifier is now called “Client number” everywhere instead of the previous organisation-specific term, and its label is configurable on the Client Registry tab (set it to “Reference number”, “Membership ID”, or anything you like — the wording then appears on the import mapping, the check-in lookup, and the desk screen). The database columns were renamed accordingly; this migration runs automatically and preserves all existing data. Example placeholders no longer reference any particular kind of organisation.

= 2.8.8 =
* Saved configurations: when restoring a configuration, any appointed user (system administrator, desk operator, or check-in operator) who no longer exists on the site is now skipped. A desk whose saved operator was deleted is restored with no operator (free to be claimed or reassigned) instead of pointing at a missing account. Deleted users were already never granted access; this also keeps the stored lists clean.

= 2.8.7 =
* New “Saved” tab under Settings: save the entire current setup as a named configuration and restore it later with one click. A saved configuration includes all locations, their topics, all desks (with their assigned operators and topic handling), check-in operators, and the Display Board, Topic Palette, Numbering & Refresh and user settings. Multiple configurations can be saved, so switching the whole system between different setups is a single button press. Restoring replaces the live locations, desks and topics (the client registry is kept).

= 2.8.6 =
* Merged the Settings “System Administrators” and “Desk Operators” tabs into a single “Users” tab, styled like a WordPress users screen. It lists everyone currently appointed with their function(s), and has a form on top to appoint a user: type 3+ letters to find them, pick a function (System administrator, Check-in desk operator, or Desk operator), and for check-in choose the location. A user can hold more than one function. Every appointed user receives an email for their function. Removing one function leaves the user’s other functions intact.

= 2.8.5 =
* Fixed the “Reassign to…” dropdown on the desk screen displaying its text clipped/cut off.
* Added a “🔔 Recall” button to the top of the desk screen that re-announces the visitor currently being served, the same as the check-in monitor’s recall.
* A recalled number now reappears in full on the public display — the large call announcement and sound play again, exactly as if the number were being called for the first time (previously a recall only re-flashed the desk tile).

= 2.8.4 =
* Desk screen: the waiting count below the visitor area no longer always shows 0 under the pull-based queue. It now shows how many waiting visitors this desk can actually call — pooled visitors whose topic this desk handles (plus no-topic visitors and anyone pinned to the desk). A topic-restricted desk also shows the location’s total waiting count alongside, so its own slice is easy to tell apart from everyone waiting.

= 2.8.3 =
* Live Queue redesigned: a top section now holds only the action buttons (Open all desks, Close all desks), separated by a horizontal line from a board below that shows the unassigned pool on the left and all desks stacked on the right.
* Removed the obsolete “Rebalance to shared queue” button and its now-unused code (the pull-based queue already feeds desks from the shared pool on demand, so manual rebalancing served no purpose).

= 2.8.2 =
* Fixed the desk operator user picker showing no dropdown and throwing console errors. The picker’s suggestion list was being moved out of place by the browser because of invalid HTML nesting (a list inside an inline element inside a paragraph); the picker now uses block-level containers so the list stays in place. The same fix was applied to the check-in operator picker.

= 2.8.1 =
* Desk operator user picker (Setup -> Desks): the type-ahead now shows a clear message when the user search cannot run (for example a permissions or connection problem) instead of showing nothing at all, so the cause is visible. The picker config is also loaded on every screen that uses it.

= 2.8.0 =
* Desk terminal links now use a per-location desk number that restarts at 1: after deleting all desks at a location and creating a new one, its link is ?vr_view=desk&vr_loc=…&desk=1 instead of continuing from the highest database id. Numbering is independent per location and reuses gaps from deleted desks.
* Adding a desk now pre-fills the name with the next default (“Desk 1”, “Desk 2”, …). It detects existing names: it continues the highest trailing number (e.g. “Counter 5” → “Counter 6”) and appends “1” to a name with no number. Leave the box unchanged to use the default, or type your own.
* New “Setup” screen consolidates Locations, Desks and Topics into one place, with a section tab for each and a per-location dropdown. The standalone menu items were removed (deep links still work and route into Setup).
* The Check-in desk controls (Open check-in screen, Manage topics + link, Switch location ON/OFF) now appear as a section on the Desks tab for each location.
* Settings is reorganised into tabs: Display Board (now including the Public display settings), System Administrators (with the appointed-admins list), Desk Operators, Topic Palette, Numbering & Refresh, Maintenance, Client Registry (moved in from its own page), and a new About tab with plugin information.
* No features were removed; existing screens and links continue to work.

= 2.7.0 =
* Statistics: fixed topics not appearing when a single location was selected (an ambiguous-column error blanked the list); topics now show correctly per location and across all locations.
* Statistics: fixed “performance by desk” showing nothing when a location was selected.
* Statistics: the Excel export now includes a Topics sheet, a Desks sheet, and a new Tickets sheet with every individual ticket — number, location, topic, desk, status, name, and the times it was issued, called, and marked done, plus wait and service durations.
* New: administrators can appoint desk operators without tying them to a specific desk (Settings -> Desk operators). After logging in, an appointed operator can claim any desk that no other operator currently occupies, and can leave a desk to free it for someone else. A desk already taken by another operator cannot be claimed.
* Fix: the Remove button for appointed system administrators now actually removes them (it was being overridden by the settings form and silently re-adding the administrator).
* Fix: the public display colour scheme is now applied correctly (custom and preset schemes were being ignored).

= 2.6.1 =
* Fix: when daily ticket-number reset is enabled, the check-in “edit next number” screen now correctly allows low numbers again at the start of a new day, instead of treating the previous day’s highest number as still in use.

= 2.6.0 =
* Security: every Visitor Routing screen now requires a logged-in WordPress user, including the public display and the check-in screen. Visitors who are not logged in see a log-in prompt; the underlying data feeds are closed to anonymous requests too.
* System administrators can now be removed from the Settings screen with a dedicated Remove button (their WordPress account is not deleted; real WordPress admins keep their access).
* Live Queue: the action buttons (Rebalance, Open all desks, and the new Close all desks) sit in one row, and the desks now wrap onto new rows at four per row, so the page no longer scrolls left-to-right. Close all desks leaves a desk that is mid-call open until that visitor is done.
* Check-in desk: the next number to be handed out can now be edited. New numbers continue from the value you set; numbers already issued — or deleted but below the latest issued — can never be reused.
* Ticket numbers can be padded to 3, 4, or 5 digits (e.g. 001, 0001, 00001) via a new Settings option.

= 2.5.0 =
* Administrators can now move a desk operator from one desk to another in one step from the Desks page (“Move operator to…” on each staffed desk). If the target desk already has an operator, the two are swapped; if it is free, the operator is moved and the old desk is freed. Same-location only, and only administrators can do this.
* The desk terminal now shows the desk’s current location under the desk name.
* Operators assigned to two or more desks get a “My desks” switcher in the terminal header to jump between their desks without needing separate links.

= 2.4.1 =
* The recent-call highlight now outlines and glows the whole desk box, not a smaller box around the number.
* The highlight is now temporary: it fades after an admin-editable duration (default 30 seconds, range 5-300). A recall restarts the timer for that number.

= 2.4.0 =
* Pull-based queue: tickets now wait in one shared per-location queue in arrival order and are only assigned to a desk when that desk calls the next visitor. This keeps waiting times even regardless of how fast each desk works, replacing the old immediate per-desk assignment that could leave one desk’s line much slower than another’s. Priority transfers and monitor moves still pin a specific visitor to a desk; those are served first.
* “Rebalance to shared queue” button (formerly Balance queues) now returns any desk-pinned waiting visitors back to the shared queue.
* Public display: visitor names can be toggled on/off (Settings -> Public display).
* Public display: optionally highlight the last 1-3 called numbers with a glowing outline (admin toggle + count).
* Public display: optional second heading line with an icon (WiFi, info, phone, clock, location, coffee) – e.g. WiFi name and password.
* Public display: colour scheme selector (Classic, Dark, Light, Ocean, Forest, or Custom colours), plus a “Scan site theme” button that fills custom colours from the active website theme.
* Check-in screen: the next number to be handed out is shown before pressing a topic button.
* Check-in desk (and admin Live Queue): recall a serving number to re-flash it on the public display, and edit a ticket’s name (the number never changes).

= 2.3.0 =
* Type-ahead user assignment: the desk operator field and the check-in operators list are now search pickers. Type 3+ letters of a name, login, or email; pick from the dropdown; the user appears as a chip (with x to remove) and is saved when you press Save. Replaces the old 1000-user dropdown and checklist, so any number of WordPress users is searchable.
* New setting (Settings -> Notifications): toggle whether a notification email is sent when a user is assigned to a desk or to a check-in desk. The “Resend invite” buttons always send regardless.
* Hardening from the assignment-logic audit: submitted user ids are now validated to exist before being stored (forged or stale ids are dropped); and deleting a WordPress user now automatically frees their desk seat and removes them from every check-in list and the system-administrator list.

= 2.2.1 =
* Screens & Links now uses the same location tabs as every other admin screen (it previously stacked all locations below each other). Desk operators without tabs of their own are automatically focused on their desk’s location so their terminal link is always visible.

= 2.2.0 =
* New role-adaptive “My screens” widget (Appearance -> Widgets) and matching [vr_links] shortcode: shows each viewer buttons for exactly the screens their role may use. Administrators and appointed system administrators get everything (admin dashboard, check-in, displays, own desk if assigned); check-in operators get their locations’ check-in screens and public displays; desk operators get the link to their own desk; everyone else (including logged-out visitors) gets the public display of enabled locations. Widget setting limits it to one location or all.

= 2.1.0 =
* New “Open all desks” button on the Live Queue toolbar: opens every closed desk of the current location in one click. Shows how many desks are currently closed and is disabled when none are.

= 2.0.0 =
* Location tabs: every admin screen now switches locations with tabs across the top instead of a drop-down. Check-in operators only see tabs for their own locations; a red dot marks a switched-off location.
* Live Queue reworked: same board as the check-in monitor — unassigned pool plus every desk queue, with per-visitor Move and Remove, and the per-desk “Move all” action retained.
* New “Balance queues” button on the Live Queue: redistributes all waiting visitors evenly across the open desks, respecting each desk’s topic abilities (a desk that cannot handle a topic is skipped for those visitors; visitors keep their arrival order within each queue; visitors no open desk can handle go to the unassigned pool).
* Check-in monitor and Live Queue now show the requested topic as a coloured pill with the topic name, matching the topic colours used elsewhere.
* Statistics: location tabs (All locations + one per location), “Export to Excel” (.xlsx workbook with Summary, Daily, Topics and Desks sheets), and “Clear statistics” which permanently deletes finished tickets for the selected scope (open queues untouched).
* Fix: “Performance by desk” showed no rows when a location filter was active — the underlying query was ambiguous after the desks join and silently failed.

= 1.9.0 =
* Topic duplication across locations. On the Topics page (managers only): every topic row has an “-> all locations” action that creates the topic at every location where it is missing and syncs colour/prefix where a same-named topic already exists.
* New “Duplicate topics between locations” panel: copy the full topic list of one location to another location or to all other locations at once, in one of two modes — “Keep existing topics” (destination topics are retained even when they differ; only missing topics are added, matched by name) or “Replace everything” (all destination topics are deleted first, desk-topic links cleaned up, then the source set is copied 1:1).
* Replace mode asks for explicit confirmation and the success notice reminds you to review desk topic assignments at the destination.

= 1.8.2 =
* Fix: client lookup no longer hides matches. Common first names matched more clients than the dropdown could show, and the cut-off was silent and alphabetical — so a client could be invisible until the query was narrowed. The dropdown now says “Showing 12 of N matches — keep typing to narrow down” whenever the list is truncated.
* Improvement: multi-word searches now work across name parts — “maria tromp” (first + last) finds Maria Helena Tromp; every word you type must match the first names, last name, or AOV number.
* Improvement: results where a name starts with what you typed rank above mere substring matches.

= 1.8.1 =
* Fix: typing in the client lookup after selecting a client no longer wipes the keystroke (the selection is cleared and typing continues normally).
* Fix: the “will be called as” preview now uses the server-computed abbreviation, so multi-word last names (e.g. “van der Berg” -> “Juan V.”) preview exactly as they will display.
* Fix: non-ASCII last-name initials are upper-cased correctly on the public abbreviation.
* The client-lookup suggestion list closes when clicking elsewhere on the screen.
* Clarified that a full reset keeps the Client Registry (delete it separately on its own page); removed dead code.

= 1.8.0 =
* Client registry: administrators can upload an Excel/CSV sheet of clients (Visitor Routing -> Client Registry) with column mapping, live progress, and atomic replace — the old registry stays searchable until the new import completes. Delete removes the registry and disables the lookup.
* Staffed check-in lookup: check-in operators can search the registry (3+ characters, like the standalone applet) and select a client. The public calling screens only ever show the first first-name plus the last-name initial (e.g. “Henk J.”); the desk operator receives the full name and AOV number with the visitor. Typing a name manually still works exactly as before, and the public kiosk is unchanged.
* The abbreviation is computed server-side from the registry record; a forged request cannot put a full name on the public display, and anonymous kiosk requests cannot query or reference the registry at all.

= 1.7.0 =
* System administrators: appoint any WordPress user as a Visitor Routing administrator (Settings → System administrators) without making them a WordPress admin. One capability grant unlocks every screen — locations, desks, topics, live queue, statistics, settings, screens & links. Appointed users receive an invitation email with all relevant links, and a per-user “Resend invite” button is available.
* Split check-in screen: when a logged-in check-in operator (or manager) opens the check-in screen, a queue monitor appears beside the check-in card showing the unassigned pool and every desk queue at that location. Waiting visitors can be moved to another desk or back to the pool, and no-shows can be removed. Anonymous kiosk visitors never see the monitor.
* Monitor actions are strictly per-location: check-in operators can only watch and act on locations they are assigned to; closed desks are refused as move targets; visitors being served cannot be moved from the monitor.

= 1.6.1 =
* Fix: location slugs are now guaranteed unique (a duplicate gets a -2 / -3 suffix automatically). Previously two locations could share a slug, silently routing one location’s kiosk visitors into the other’s queue.
* Fix: the display board now reacts when its location is switched off mid-session — it shows the “not taking visitors” notice and resumes automatically when the location is switched back on. Previously a running kiosk kept showing the live board after the location was disabled.
* Multi-location isolation audit: verified ticket numbering, routing, queue operations, transfers, statistics, rate limiting, and reset are all strictly per-location with no cross-location interference.

= 1.6.0 =
* Robust button feedback in the desk terminal: every action button now shows a spinner immediately on press, other buttons grey out, the pressed button briefly flashes green (success) or red (error) before the screen refreshes. A short minimum lockout prevents accidental double-action.
* Server-side double-close protection: Close and Close & call next now carry the operator’s currently-displayed ticket id, and the server refuses the action with a clear message if the queue has already moved on. This eliminates the “I pressed twice and accidentally closed the next visitor” scenario.

= 1.5.3 =
* Fix: visitor name on the call screen no longer gets clipped vertically when the active theme sets a tight inherited line-height.

= 1.5.2 =
* Closed desks are out of rotation everywhere. Renamed “Pause desk” to “Close desk” in the operator terminal. Closed desks cannot accept new tickets, transfers, or reassignments, and no longer appear in transfer/reassign/reroute pickers.
* Fix: “Move all visitors” now works correctly. Inline destination menu replaces the prompt dialog; moves to the unassigned pool and to other desks both work; closed desks are excluded; same-location is enforced.
* Fix: the call-screen overlay no longer stacks when a second visitor is called within a few seconds — only one overlay is ever visible.
* Call screen now also shows the visitor’s name (when provided at check-in) below the big number.
* Locations page: per-operator “Resend invite” button for check-in operators, matching the existing button on the Desks page.

= 1.5.1 =
* Hardened cross-location boundary: a desk now strictly only helps visitors at its own location. Operator reassign, admin bulk reroute, and admin drag-and-drop all reject attempts to move a ticket to a desk in a different location. Editing a desk no longer accepts a different location_id — to move a desk, delete and recreate it at the target location.
* Topic-to-desk assignments are filtered to the desk’s own location, preventing stale or tampered POSTs from linking a desk to topics belonging elsewhere.
* Desk edit form now clearly shows which location the desk belongs to.

= 1.5.0 =
* New topic colour system: choose from a curated palette of 10 medium-tone colours that read clearly as background chips with white text.
* Administrators can edit the palette (Settings → Topic palette) and restore the defaults with one click.
* Fix: warning message now shown when refusing to delete the last remaining location (was silent before).
* Fix: closed a race window in the daily ticket-numbering reset by collapsing the date-flip and counter increment into one atomic UPDATE.

= 1.4.0 =
* New Statistics page: visitors helped, service mix by topic, reroute counts, average and longest wait/service times, and a per-desk breakdown — filterable by date range and location.
* Daily volume chart on the report (helped, removed, still-open).
* Reroute tracking added at every desk-change path (operator reassign, priority transfer, bulk reroute, admin drag-and-drop).

= 1.3.0 =
* Operator terminal: transfer a specific waiting visitor to another desk with priority — the transferred ticket jumps to the front of the target queue.
* Admin Live Queue board: remove individual tickets (no-show cleanup). Removed tickets are kept as “cancelled” for audit and never reappear in queues or stats.
* Fix: when reassigning the active visitor to another desk, the ticket now correctly lands at the top of the target queue (previously could collide with another waiter at position 0).

= 1.2.0 =
* Multiple locations: each location runs its own visitor routing system — its own desks, topics, queue, ticket numbering, on/off switch, and check-in operators.
* New Locations admin page with per-location enabled toggle and check-in operator assignment.
* Check-in operators are scoped per location and receive an invitation email with the direct check-in link for their location.
* Live Queue, Desks, Topics, and Screens & Links pages all gain a location switcher.
* Per-location ticket numbering is atomic and collision-free; each location numbers independently.
* Automatic migration from 1.1.x: a “Main” location is created and all existing desks, topics, and tickets are moved into it; existing on/off state and check-in operators are preserved.

= 1.1.0 =
* Operator terminal: added a one-press “Close & call next” button.
* Assign any WordPress user to a desk (the operate capability is granted automatically) with a searchable picker.
* Master on/off switch and a full “start over” reset; “Reset the day” now restarts numbering at 001.
* Faster, query-light routing and an atomic, collision-free ticket counter.
* Configurable check-in rate limit (staff are never throttled).
* Assets are now versioned by file modification time so updates always load (no stale cached scripts/styles).
* Fix: ticket numbers could all show as 001 due to an unreliable insert-id read.

= 1.0.0 =
* Initial release: desks, topics, automatic topic-aware round-robin routing, drag-and-drop live queue, reroute & reassign, operator terminals, display board with flash + ping/custom sound, optional wait-time estimate, and daily ticket reset.

== Upgrade Notice ==

= 2.8.9 =
Adds a first-run setup wizard and renames the client identifier to a configurable, generic “Client number”. A database migration runs automatically and preserves existing data.

= 2.8.8 =
Restoring a saved configuration now cleanly skips appointed users who no longer exist.

= 2.8.7 =
Adds a Saved configurations feature (Settings -> Saved) to save and restore complete setups with one click.

= 2.8.6 =
System Administrators and Desk Operators settings are merged into one “Users” screen with a type-ahead appoint form and per-function emails.

= 2.8.5 =
Adds a Recall button to the desk screen, makes recalls re-announce in full on the public display, and fixes the clipped “Reassign to” dropdown.

= 2.8.4 =
The desk screen’s waiting figure now reflects the visitors this desk can actually serve, instead of always showing 0.

= 2.8.3 =
Live Queue layout reorganised (buttons on top, pool left / desks right) and the obsolete Rebalance button removed.

= 2.8.2 =
Fixes the desk operator user search (no dropdown / console errors) caused by invalid HTML nesting in the picker.

= 2.8.1 =
Improves the desk operator user picker so search problems are shown instead of failing silently.

= 2.8.0 =
Desk links now restart at 1 per location, desk names auto-fill, and the admin is reorganised into a Setup screen and a tabbed Settings screen (Client Registry and an About page included). Schema upgrade backfills per-location desk numbers automatically.

= 2.7.0 =
Fixes statistics topics/per-desk views and the Excel export (now includes per-ticket detail), fixes system-administrator removal and the display colour scheme, and adds self-service desk claiming for appointed operators. No database migration required.

= 2.6.1 =
Bug-fix for the editable next-number feature under daily reset. Safe drop-in upgrade.

= 2.6.0 =
Important: the public display and check-in screens now require login. Adds removal of system administrators, a tidier Live Queue with Close-all-desks, an editable next ticket number, and selectable number length. No database migration required.

= 2.5.0 =
Adds one-click operator desk-switching for administrators, shows the location on the desk terminal, and a desk switcher for operators assigned to multiple desks. Safe drop-in upgrade.

= 2.4.1 =
Recent-call highlight now glows the whole desk box and clears after an editable time (default 30s). Safe drop-in upgrade.

= 2.4.0 =
Major queue change: visitors now wait in one shared queue and desks pull the next visitor on demand, evening out waiting times. Adds display name toggle, recent-call highlight, second display line, colour schemes with site-theme scan, next-number preview, and recall/edit-name for the check-in desk. No database migration required.

= 2.3.0 =
Type-ahead pickers for desk and check-in operator assignment, an on/off toggle for assignment emails, and cleanup of assignments when WordPress users are deleted. Safe drop-in upgrade.

= 2.2.1 =
Location tabs on Screens & Links. Safe drop-in upgrade.

= 2.2.0 =
Adds the role-adaptive links widget + [vr_links] shortcode. Safe drop-in upgrade.

= 2.1.0 =
Adds a one-click “Open all desks” action on the Live Queue. Safe drop-in upgrade.

= 2.0.0 =
Location tabs everywhere, reworked Live Queue with automatic queue balancing, topic colour pills, Excel export and clear option for statistics, and a fix for per-desk statistics under a location filter. Drag-and-drop ordering on the old Live Queue board is replaced by per-visitor Move controls.

= 1.9.0 =
Adds one-click topic duplication across locations (merge or full replace). No schema changes; safe drop-in upgrade.

= 1.8.2 =
Client-lookup search fix: truncated result lists are now labelled, multi-word first+last searches work, and prefix matches rank first. Safe drop-in upgrade.

= 1.8.1 =
Bug-fix release for the 1.8.0 client lookup. Safe drop-in upgrade, no schema changes.

= 1.8.0 =
Adds an administrator-managed client registry with staffed check-in lookup. Public screens show only an abbreviated name; desks receive full name + AOV. Schema migration runs automatically on first admin load.

= 1.7.0 =
Adds appointable system administrators (no WordPress admin role needed) and a queue monitor beside the check-in screen for logged-in check-in operators. Safe drop-in upgrade, no schema changes.

= 1.6.1 =
Multi-location isolation fixes: unique location slugs enforced, and kiosk displays now react when their location is switched off. Safe drop-in upgrade.

= 1.6.0 =
Adds spinner-based press feedback to the operator terminal and a server-side check that prevents an accidental double-press from closing the wrong visitor. Safe drop-in upgrade.

= 1.5.3 =
Small CSS fix for the call-screen visitor name. Safe to apply.

= 1.5.2 =
Bug-fix release. Closed desks are now strictly out of rotation; “Move all visitors” works; the calling overlay never stacks; calls show the visitor name. Safe to apply.

= 1.5.1 =
Tightens cross-location enforcement: desks now strictly serve only their own location through every routing path. No data changes; safe to apply.

= 1.5.0 =
Topic colours now come from an editable 10-swatch palette. Existing topics keep their stored colour until you next edit them — pick a new swatch to update.

= 1.4.0 =
Adds a Statistics report. Reroute counts start from zero for any tickets created before this upgrade — historical wait/service timing is preserved.

= 1.3.0 =
Adds priority transfer between desks and the ability to remove no-show tickets from the queue.

= 1.2.0 =
Adds multi-location support. Your existing setup is automatically migrated into a default “Main” location with no data loss; you can immediately add more locations afterwards. Hard-refresh kiosk screens once after upgrading so the latest scripts load.

= 1.1.0 =
Adds “Close & call next”, any-user desk assignment, on/off and full-reset switches, and fixes ticket numbering. Clears stale cached assets automatically.

= 1.0.0 =
First public release.