Skip to content

Panel Customers: QA fixes, prototype parity, order-value chart, and UX polish#2565

Merged
glennjacobs merged 19 commits into
2.xfrom
fix/panel-customers-qa
Jul 17, 2026
Merged

Panel Customers: QA fixes, prototype parity, order-value chart, and UX polish#2565
glennjacobs merged 19 commits into
2.xfrom
fix/panel-customers-qa

Conversation

@glennjacobs

Copy link
Copy Markdown
Contributor

Summary

A full pass over the panel's Customers section: browser-QA fixes, design-prototype parity on the index and edit pages, a new charting primitive on the add-on surface (spec 0050), and a series of UX refinements.

QA fixes

  • Address forms show inline validation errors (the edit form previously failed silently); every field is covered via a shared AddressFormFields
  • Index search also matches linked user emails, as its placeholder promises
  • KPI strip shows real values instead of hardcoded placeholders
  • Dead Export button removed; Docs links to the docs site
  • Unlink-user confirm says "Unlink user", not "Delete"
  • Activity timeline translates descriptions, formats timestamps, and covers address/user-link events (logged by the core customer actions, same pattern as the order actions)
  • Accessibility: programmatic labels on all customer form inputs (FieldLabel for + ids), named back link, stored titles outside the base list still display

Prototype parity

  • Index: order stats per row (placed-order count, total spend, latest order) via correlated subqueries; linked email under the customer name; tinted avatars; breadcrumbs on create/edit
  • Edit: single-user email (or user count) in the header; Lifetime stats side card; read-only Order history tab; editable Admin notes side card backed by a new customers.admin_notes column (folded into the flat baseline per the alpha migration policy)
  • KPIs: avg lifetime value with a 30-day delta chip; the KPI block is cached for 5 minutes since every value is a whole-table aggregate

Charting (spec 0050)

  • TimeSeriesChart: dependency-free SVG area/line chart on the public add-on surface — crosshair tooltip, keyboard navigation, draw-in animation (prefers-reduced-motion aware), theme-token styling
  • Order-value-over-time card on customer edit: 12m default, zooming to 3y/5y/10y via partial reloads on a validated chart_range param

UX polish

  • Add/edit address moved into modals (shared Dialog)
  • FlashMessage component: animated, dismissible, 6s auto-dismiss with hover pause; swapped into all six flash call sites and exported to add-ons
  • Clear-filters text link and a clearable search input (TextInput clearable)
  • KpiCard renders as a static div unless a click handler is bound

Contract-surface notes for review

  • UpdatesCustomer::execute() third parameter is now ?array $customerGroupIds = null — null leaves group membership untouched, [] still clears (used by the notes endpoint; all existing callers pass explicitly)
  • Core customer address/user-link actions now log explicit activities on the customer
  • New public add-on exports: TimeSeriesChart, FlashMessage; TextInput gains clearable, FieldLabel gains for, Select gains invalid
  • New baseline column customers.admin_notes (alpha policy: baseline edited in place, no change migration)
  • Spec 0050 added (accepted, implemented); spec 0049's screen description updated

Translations

All new keys mirrored across the 16 locales with real translations; orphaned keys removed. Every locale file parses with identical key counts.

Tests

  • Suites green: core (950), panel (254), filament (41), admin (235); PHPStan and Pint clean
  • Frontend: 56 vitest tests (chart, flash, clearable input, KpiCard modes), vue-tsc, panel + example add-on builds
  • Every feature was also exercised end-to-end in a browser against the host app

🤖 Generated with Claude Code

glennjacobs and others added 17 commits July 16, 2026 19:06
Index:
- KPI strip now shows real counts (total, new in 30 days, business,
  linked to a user account) instead of hardcoded placeholder values
- Search also matches linked user emails, as the placeholder promises
- Remove the non-functional Export button; Docs links to the docs site
- Row avatars build initials from first/last name, not the full name
  with title

Edit:
- Address forms (new and edit) share AddressFormFields, cover every
  address field (title, line 3, state, contact email/phone, tax id)
  and show inline validation errors — the edit form previously failed
  silently
- Unlink-user confirm button says "Unlink user" instead of "Delete"
- Activity timeline translates descriptions, formats timestamps, and
  picks icons for the customer events
- Title selects translate their labels and keep an unknown stored
  title as a selectable option instead of displaying blank

Core:
- Customer address create/update/delete and user link/unlink actions
  log an explicit activity on the customer (same pattern as order
  actions), so the panel timeline reflects them

Accessibility:
- FieldLabel renders a real <label for> when given a target id; all
  customer form inputs are now programmatically labelled
- Back link on the create page has an accessible name

Translations: new keys mirrored across all 16 locales.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Both pages went straight to PageHeader; they now render the same
breadcrumb bar as the index (Sales > Customers > current page) with
the Docs link in the actions slot.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Header shows the linked user's email when the customer has exactly
  one storefront user; it stays out of the edit form (the user record
  owns it)
- Lifetime stats side card: total spend, order count, average order,
  and latest order date over placed orders, reported in the default
  currency via each order's captured exchange rate (the prototype's
  duplicate top-of-page stat strip is intentionally not implemented)
- Order history tab: read-only list of placed orders (reference, date,
  lifecycle status, total); rows link nowhere until an Orders section
  exists
- Notes side card with inline editing, backed by a new nullable
  customers.notes column (new migration on top of the flat baseline)
  and a panel notes endpoint
- UpdatesCustomer now takes a nullable group set: null leaves group
  membership untouched (used by the notes endpoint), an empty array
  still clears it
- No "email customer" action, per review
- Spec 0049 updated: order history and purchase stats are no longer
  deferred

Translations: new keys mirrored across all 16 locales.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
With one linked user the header shows their email; with several there
is no single address to show, so it shows "{n} users" instead
(pluralised via vue-i18n, keys in all 16 locales).

Also fold the customers.notes column into the flat baseline migration:
while v2 is in alpha, schema changes update the existing migrations
rather than adding change migrations (CLAUDE.md updated to match).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The sidebar card, edit action, empty state, and flash now say "Admin
notes" across all 16 locales so the field's audience is explicit. The
customers.notes column and lang keys are unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Matches the "Admin notes" label: baseline migration column, model
property, notes request/controller field, edit page prop and form all
use admin_notes. Route names and lang keys are unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Draft spec for a dependency-free TimeSeriesChart on the add-on surface
and an order-value-over-time card on the customer edit page (12m
default, zoom to 3y/5y/10y via partial reloads).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…customer edit (spec 0050)

- TimeSeriesChart: dependency-free SVG area/line chart styled with the
  panel theme tokens — recessive halved gridlines, thinned x labels,
  crosshair hover tooltip (value leads), keyboard navigation, required
  aria label. Exported via ui.ts, mirrored in @lunarphp/panel index.js,
  types regenerated; the example add-on renders one to prove the
  public surface.
- Customer edit gains an "Order value" card above the details form:
  placed-order value bucketed over a validated chart_range (12m
  monthly default; 3y/5y quarterly; 10y yearly), zero-filled and
  valued in the default currency on the same basis as the lifetime
  stats. The range switcher partial-reloads only the chart prop and
  holds the previous render at reduced opacity while in flight.
- Translations for the chart card across all 16 locales.
- Spec 0050 accepted with all slices landed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The line draws left to right (dash math normalised via pathLength) and
the area fades in beneath it; keying the series paths on the data
replays the animation when a range switch swaps the buckets, and
prefers-reduced-motion disables it entirely. Also keep a full step of
clearance before the final x label so long ranges don't collide at the
right edge.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The inline form rendered below the address cards and was easy to miss.
"Add address" now opens the panel Dialog (footer submit associated to
the form via the form attribute); errors keep the dialog open, success
closes it, and stale errors clear on reopen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The edit form no longer swaps in place of its card; the Edit action
opens the shared Dialog prefilled, titled with a new edit_address key
across all 16 locales.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Animated slide/fade in and out, a dismiss button, auto-dismiss after
6s (paused while hovered), and re-triggering on every server response
so repeat saves with identical text still flash. Errors and the
forgot-password status pass timeout 0 and persist until dismissed.
Swapped into the customers pages, SettingsShell (success + error),
account security, forgot password, and the example add-on; exported on
the public add-on surface.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Rows carry per-customer order stats via correlated subqueries on the
  same placed-order, default-currency basis as the edit page: placed
  order count, formatted total spend, and latest order date replace the
  Created column (recency stays available through the sort dropdown)
- The first linked user's email shows under the customer name, falling
  back to the account ref
- Avatars take one of the three soft theme tints, assigned by customer
  id so a customer keeps their colour across reloads
- Column labels reuse the existing stat_* lang keys; the unused
  column_created key is dropped from all 16 locales

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The fourth KPI card is now the prototype's avg lifetime value: one
aggregate over placed orders (default-currency basis via captured
exchange rates) divided by distinct customers with orders, plus a
delta chip against the average as it stood 30 days ago (neutral tone
when negative). The whole KPI block is cached for five minutes since
every value is a whole-table aggregate and the strip does not need to
be second-accurate on large stores. Replaces the interim "can sign in"
count; lang keys swapped across all 16 locales.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sits to the right of the filter dropdowns, only while a search, filter,
or add-on filter is active; plain underlined text, reusing the existing
clearFilters behaviour and lang key.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
An inline X shows while a clearable input holds a value (hidden when
disabled); clicking it empties the value and refocuses the input. New
common.clear key across all 16 locales.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The stat tiles looked clickable (button element, hover/press styles)
but did nothing. A card now only becomes a button with hover and press
affordances when an onClick is actually attached, so genuinely
interactive tiles keep the treatment.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
glennjacobs and others added 2 commits July 17, 2026 11:36
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@glennjacobs
glennjacobs merged commit 308529b into 2.x Jul 17, 2026
22 checks passed
@glennjacobs
glennjacobs deleted the fix/panel-customers-qa branch July 17, 2026 10:39
@github-project-automation github-project-automation Bot moved this from Todo to Done in Roadmap Jul 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant