Skip to content

feat(storefront): UX overhaul — context switcher, network screens, cards, panels, promotions hub, create order - #137

Merged
roncodes merged 134 commits into
release/v0.4.25from
feat/storefront-ux-overhaul
Oct 12, 2026
Merged

roncodes merged 134 commits into
release/v0.4.25from
feat/storefront-ux-overhaul

Conversation

@roncodes

@roncodes roncodes commented Oct 10, 2026 •

Copy link
Copy Markdown
Member

Storefront UX overhaul (single PR)

Replaces the stacked PRs #121–#136 with one branch targeting release/v0.4.25. Implements the Storefront redesign from the Claude Design canvas on @fleetbase/ember-ui primitives: store/network context switcher and context-scoped navigation, network overview / stores / orders / trucks / customers / categories / settings, resource cards on Layout::Resource::Card, order details panel, create order form (console orders through the app's checkout), products page, catalog editor, product editor, customer panel, promotions hub and builder, trucks page and panel, settings shell with a section rail, dashboard attention strip, network join flow.

Every screen was exercised in a running console in both store and network context (dark and light) before being marked done.

What changed since the last review

  • Crashes fixed: truck create/edit and customer panel in network context, create-order line configurator (add-on relations read through the proxy), schedule manager on lowercase day names, sidebar group inside the wormhole.
  • One palette, type scale and radius set for the stylesheet (tokens documented at the top of storefront-engine.css); one Storefront::ViewToggle; product grid card on the shared card anatomy.
  • Settings: every page on one section anatomy (the old SettingsContainer shell is gone), Hours summary per location, Danger zone (delete store), rail counts; network settings rebuilt on the same shell.
  • Dashboard header shows the store's open / closed state from its locations' hours in the store timezone, plus New order.
  • Sidebar: "Member of" (store context) and "Your stores in this network" (network context) cross-link groups.
  • Network: Categories page; Stores page per the board (status, orders 7d, share bar, joined, invitation rows) with suspend / reinstate (new network_stores.status); catalog card actions menu; trucks Map button and panel Location tab.
  • Promotions: engine honours applies_to.segment once the customer is known.
  • Approval queue: an invitation sent with "require my approval" now creates a pending membership on accept (hidden from the app); the Stores page gets an Awaiting approval tab with Approve / Reject row actions and a bulk Approve.
  • Per-catalog price and availability: the catalog editor's product rows show the effective price with a click-to-set catalog price and a sold-here toggle; the app receives the catalog price and availability, and cart lines added with a catalog keep that price.
  • Create order from the network context: a store step lists the network's active members, the rest of the form is the store form; the network Orders page gets a New order button.
  • Dead coupons, broadcast and pages routes removed; muted light-mode text darkened to gray-500 for 4.5:1 at 12px.
  • Board parity pass (every screen compared with its mockup): dashboard header leads with the store name and open state, Orders nav badge from the attention counts, recent-orders widget with the board's columns and a next-action button; Products titled Products with a sort control and a bulk Add to catalog; Settings gains a timezone field; Catalogs page gets status tabs and out-of-stock counts; customer overview shows the customer's segments; promotions table is selectable with bulk pause / resume / end; truck cards say which catalog is serving and when an offline vehicle last reported, truck panel opens the vehicle; network overview shows member cards with membership badges; the shareable join link now works end to end (token link, link joins with approval, Regenerate / expiry / category / auto-approve); customers page gets a Cards view with a customer card.

Server / API

  • Migrations: 2026_10_11_000000_add_status_to_network_stores_table (status default active, also pending for memberships awaiting approval) and 2026_10_11_000001_add_overrides_to_catalog_category_products_table (price, is_available, both nullable).
  • New internal endpoints: POST networks/{id}/suspend-stores, reinstate-stores, approve-stores, reject-stores ({ stores: [uuid] }); membership changes invalidate the cached store listings (ApiModelCache).
  • Catalog payloads: each category accepts product_overrides ({ <product uuid>: { price, is_available } }, an explicit map is authoritative) and returns it internally; the public catalog product carries price (effective), store_price, catalog_price, is_available (effective) and catalog_available.
  • Network join link: POST networks/{id}/shareable-link (enabled, regenerate, expires_in_days, category_uuid, requires_approval) manages the link; GET/POST networks/join/{uri} now also resolve a link token and create a link invitation + membership on accept; invitations carry via (email | link) and store_name; the overview's members carry category and membership_status; customer insights carry segments.
  • Cart: POST carts/{id}/add/{product} accepts catalog (catalog id); the line keeps catalog_id and is priced at the catalog price on add and update; a product hidden in that catalog is refused. Checkout itself is unchanged: it prices from the cart lines. Needs human review (needs-human-review).
  • Store resource: network_status (internal, when network is given); networks with with[]=networks.
  • Public API: suspended members are excluded from the network's store and product listings.
  • OrderFilter: customer / customer_uuid; ConsoleOrderController quote/place for console orders.
  • Bug: store-locations?store_uuid= was ignored by the API (filter method is store); the console now queries with store=.

Screen audit after the 11 Oct review

  • Reusable Storefront::PageHeader (avatar or icon, name, status badge, meta line, search, actions) and Storefront::SectionCard replace ContentPanel on the network overview, stores, orders and trucks pages; KPI tiles are compact (uppercase 11px label, single-line value).
  • Network overview: header with logo, Online badge, currency · timezone · id; member cards with one ellipsis menu; the body scrolls (the overflow-y-scroll-i class is now defined).
  • Network stores: Filters menu in the header (status, category), table inside a bordered card with an outer margin and a Showing footer, short bulk labels with a right-aligned Clear link, category column as an inline dropdown that assigns on pick.
  • Network orders: compact rows, fulfillment as a badge, group rows expand to their child order rows (chevron in the reference cell), reference cell shows the checkout id with "N orders · M items", Filters (status, fulfillment, store) and a Columns picker in the header, Group by checkout / Flat as a plain segmented control, table card with pagination.
  • Trucks: card header is the vehicle pill with the driver as subtitle, footer has edit plus one ellipsis menu (assign catalogs, open vehicle, delete), meta reads " service area · " and "N catalogs · M orders today" (or last online); the panel header is the truck image with id · owner · online state on the left and Open vehicle / ellipsis / close on the right, tabs are flush, Details holds the form, the catalogs list and a Today block (orders, revenue, avg prep) with a Leaflet position map; new Orders tab (orders stamped with the truck) and Location tab map; the Map button switches the page to a map of the trucks with a reported position instead of leaving for Fleet-Ops.
  • Promotions in the network context: networks/:id/promotions (promotions, campaigns, segments, redemptions) reuse the hub templates and controllers with the network as owner; the builder gains a Stores target for network-owned promotions; one Promotions entry in each sidebar as the board specifies. The networks list query params are prefixed (n_page, n_query, …) so nested pages keep the plain keys.
  • Settings: the rail scrolls the settings pane, not the console; content padding and width normalised. "Your stores in this network" sidebar group removed (not on the board).
  • Order action labels in the actions menu are translated (were hard-coded).
  • Create order form rebuilt to its board: flat numbered sections with uppercase headings, customer pill, location and address option cards, items as a table behind an Add products button, a gray summary rail (Promotion, Payment select, Tip, Summary) with a sticky Place order footer; the network variant keeps its store step.

Server / API (11 Oct additions)

  • FoodTruck resource carries today (internal only): { orders, revenue, currency, avg_prep_minutes } for orders stamped meta.food_truck_id created today in the owner's timezone; the truck list eager-loads vehicle.driver.
  • OrderFilter::foodTruck (?food_truck=<public id>), ProductFilter::network (?network=<uuid>, products of the network's member stores).

12 Oct review (store context, network context, UI and skeletons)

  • Store context:
    • The attention strip is compact.
    • Opening a panel such as a new order, a customer or a product no longer reloads the list behind it.
    • The create order Deliver-to field puts its help under the input, and a new address is saved to the customer as a Fleet-Ops place.
    • Products is a single sidebar entry.
    • The product card was rebuilt.
    • The smart groups read Unavailable instead of Out of stock, and On sale has its icon.
    • The products list view is a real table with select-all and single-line identity cells.
    • The products skeleton follows the chosen view.
    • The product form is one page with a rail.
    • Add-ons are managed in a panel.
    • The catalogs header has no subtitle or rule, and the catalog editor rows line up on one line.
    • The customer panel was rebuilt with in-panel edit, a working orders tab and a New order action that preselects the customer.
    • The new truck panel reads cleanly.
    • Promotion status tabs filter instantly and show counts. A promotion past its end date reads Ended, as the server applies it.
    • Builder choices sit on the step surface, BOGO fields are aligned and the footer is flush.
    • Settings section headings are compact and uppercase, and the save bar spans the content.
  • Network context:
    • The overview shows member stores four to a row.
    • Recent orders list each order with customer and store identity cells, row hover, row click into the order panel and right-aligned totals.
    • Network and store Customers lists now come from Storefront's customers endpoint. They used Fleet-Ops' combined customers-and-vendors query, which ignored the store and network scope, sorted by name and never paged.
    • The table row menu's Edit opens the customer's details form.
    • Network orders are left for the next pass, as agreed.
  • UI:
    • Table card footers keep the row rhythm at 37px.
    • Rows sit on the card surface.
    • Row hover reaches the sticky ID and action cells. The old dark rule never matched because the wrapper class sits on the table wrapper itself.
    • Dashboard widget rows are single-line, with a Placed column.
  • Skeletons: every page loads with a skeleton drawn from that page. Each skeleton follows its page's header, avatar, subtitle and action count, its tab strips, columns and checkbox, its identity column, its cards, and the products and settings rails. Network pages have their own skeletons, and the network promotions pages reuse the store templates through loading routes. The new ember-ui arguments are in release/v0.4.6: skeleton loading components and dark palette tiers ember-ui#189.
  • ember-ui: the button and input restyles were reverted until the platform pass.

Server / API (12 Oct)

  • The network overview's recent_orders lists each order, not one per checkout. Each entry carries customer_id, customer_photo_url and store_logo_url (internal endpoint).
  • No new endpoints. The console now reads GET storefront/int/v1/customers?storefront=|network= (existing) for its customer lists.

12 Oct, second review

  • Root cause of the network, categories and notification channel errors: core-api's Comment::publish left every model unguarded in the Octane worker after a failed note, so request parameters became where clauses. The fix is fix(comments): a failed comment publish no longer leaves every model unguarded core-api#298. Separately, the products page no longer sends its view mode to the categories query.
  • Customer notes: they save. The comment thread now sends the contact's uuid (release/v0.4.6: skeleton loading components and dark palette tiers ember-ui#189).
  • Order panel:
    • Items load when the order is opened from a list, not only after a refresh.
    • The tracking number is the title, with the ID beside it.
    • The status steps have room around them, and the customer shows their phone.
    • Header actions share one compact height.
    • The Store section is now a store profile: backdrop, logo, name, online state and stacked facts.
    • Section headings are compact and aligned with their content.
    • Breakdown strings are translated.
  • Tracking number: the Order column (with ID beside it), the dashboard widget, the customer panel, checkout siblings, truck orders, the incoming-order modal and the network overview all use it.
  • Tables and dashboard:
    • Row buttons share one 26px height.
    • Dashboard widgets use the Orders page's identity cells.
    • Delivery badges have their own colour.
  • New order:
    • The "+" opens an address modal and saves the address to the customer; a chosen address reads as a place card with Change address.
    • Tips add 20% and 30%.
    • The notes field is full width with room below.
  • Customers: Places has a real empty state and Add address, and a row's Edit opens the form.
  • Products:
    • Cards keep their full height in the scrolling grid. They have a 4:3 image or a placeholder, a two-line title so prices align, and hover and selection.
    • The list pins the Product and actions columns.
    • Skeletons follow the grid and the bare list.
  • Product form:
    • Name and SKU have placeholders.
    • Video links are rows with thumbnails.
    • Variants and add-on groups share one group anatomy.
    • Availability is a compact week, which also applies to catalogs and store locations.
    • Translations is its own section: language chips over the name and description, with the originals as hints.
    • The save bar counts every change.
  • Catalogs, trucks and settings:
    • The catalog editor's actions header no longer overflows.
    • A long truck badge ellipsizes before the buttons move.
    • Card buttons share one height.
    • Settings sections are single cards with compact headings, and nested panels align with their heading.
  • Skeletons: card grids use the real grid's breakpoints.

12 Oct, third review

  • Store context:

    • Order IDs line up with their row.
    • The identity strip uses 1.2/1.2/1.2/0.8 columns.
    • The products grid fits below its toolbar with room to scroll the last row clear.
    • Catalog and truck card footers sit on the card's bottom edge, and card buttons share one line.
  • Network overview:

    • Store cards are four across, with the store's backdrop and its logo over it.
    • The card action reads "Manage this store", and the menu adds "View store details".
    • The loading skeleton is shared by every way into the overview, including coming back from another network page, which used to show a header-over-table skeleton.
  • Store details (new, networks/:id/stores/:store_id):

    • A header with "Manage this store".
    • The store's week (orders, revenue and its share of the network), plus orders and customers through the network.
    • Customers show their order count and open the customer panel.
    • Reached from the stores table name, the overview cards and a customer's "Stores ordered from" rows (now with hover and a chevron; an order placed on the network itself stays static).
    • The sidebar keeps Stores active.
  • Network orders:

    • The store cell is one line.
    • A multi-store checkout shows round store logos with the summary card on hover, including the network when an order was placed on it.
    • The Checkout/Order and actions columns are sticky.
    • The "part of chkt_…" subtext is gone (it remains in the order panel header).
  • New order: store, location and address cards use the promotion builder's choice-card styling in both themes.

  • Hover: recent order rows (customer panel and network overview) have a visible hover in light.

  • Stores table: the category caret sits at the far right.

  • Switching to a network:

    • It lands on the overview.
    • The networks list (every network with gateways and channels) is no longer queried on the way into a network, so no network cards flash above the skeleton.
    • The list loads when it is shown.
  • Server: the network overview's members include backdrop_url.

  • Network orders, final polish:

    • Every order row is one line.
    • The Order column shows the tracking number, with the order id on hover.
    • Store and driver are identity cells (20px image, name only, no phone or currency).
    • Totals drop the tip line, which moves to a hover title; a checkout row keeps one tip line.
    • A checkout's stores are evenly spaced 20px logos.
    • The child branch follows the view, so Flat shows none (it used to stay on the records after grouping).
  • Trucks:

    • Saved catalogs stay on the truck. FoodTruck::setCatalogs loaded the relation before syncing and never reloaded it, so the save answered with the old catalogs, and the next save deleted the new row. It now reloads the relation and clears the cached truck lists, because a catalogs-only change fires no updated event. New Pest test, which fails without the fix.
    • The card title opens the truck. It used to open Fleet-Ops' vehicle panel, which crashed inside Storefront (vehicle/panel-header not found). The general fix for opening Fleet-Ops records from other engines is fix(descriptors): open Fleet-Ops records on their page outside Fleet-Ops fleetops-data#88.
  • Incoming order modal, redesigned as a ticket:

    • Store, total and payment up top.
    • The tracking number with status, fulfillment and timing chips.
    • The route as stops; a pickup shows one stop and needs no driver.
    • Customer and driver side by side.
    • The customer's note, when there is one.
    • The order panel's items and totals, now a shared Storefront::Order::LineItems (zero tips are left out).
    • The label codes for handover.
    • Accept is green on the socket alert too.

Validation

  • npx eslint addon app tests, npx ember-template-lint ., stylelint on the stylesheet: clean.
  • ember test (12 Oct, third review, customer-panel integration test excluded): 275 tests, 28 skipped, 246 pass, 1 failure in a new route test's stub, fixed and rerun green with the affected modules (20/20). New tests: network order cells (single-line store, avatar stack, reference without the checkout), the networks list skip and reload, Stores staying active on store details.
  • ember test (12 Oct, second review, customer-panel integration test excluded): 271 tests, 28 skipped; three tests written against replaced markup were updated and pass. Earlier run: 271 tests, 28 skipped, the rest pass after the 12 Oct fixes. That run's 7 failures were stale tests: the error controller resolving outside the engine, the widget list and the network customers query. Each was fixed and rerun green. The customer-panel integration render still hangs in this environment for reasons unrelated to this branch, as before.
  • ember-ui skeleton tests: 15 pass.
  • Server: php scripts/pest-runner.php --log-junit … server/tests on this branch: 657 tests, 17 failures + 27 errors, all remaining failures pre-existing on the base commit (missing SocketCluster classes in the local vendor, invites meta column, etc.; the base commit has 18 failures + 43 errors). No new failures. New tests: promotion segments, hidden memberships in the public store listing, catalog overrides (category sync, resource, cart pricing). The Pest console printer prints nothing under PHP 8.4 locally, so results were read from the JUnit log.
  • php-cs-fixer clean on the touched server files. phpstan is not run in CI; its pre-existing findings on PromotionEngine are unchanged.

Not in this PR (follow-ups)

  • Multi-store carts from the console (one checkout, one order per store): the network create order places a single-store order; the app keeps the multi-store path.
  • ember-ui extractions (Resource::Card generics, Form::SaveBar, Form::Steps, Notice, ContextSwitcher, HoursStrip) once the patterns settle here; they live in the ember-ui package.
  • The nine non-English locales still miss the settings and notification keys; they need translators, not a code change.
  • Board items the API cannot honour yet, left out rather than faked: settings toggles for reviews, account requirement and scheduled orders (no server rule reads them), truck pickup radius, network order bulk dispatch, Export on the orders and trucks pages (no export endpoint for these resources).
  • Saved filters on order and product lists (P2 on the board).

Console dependency

  • Extension loading templates only render if the console's console/app/routes/application.js loading action returns true. That change is on fleetbase/fleetbase release/v0.7.69 (e50764aa1).

Docs / API spec

User-facing behaviour, navigation and internal endpoints change: fleetbase.io storefront docs (networks, settings, promotions, create order) and the fleetbase/postman collections (network overview members[].backdrop_url, suspend/reinstate/approve/reject, console order quote/place, order customer and food_truck filters, product network filter, food truck today, store network_status, catalog product_overrides, cart add catalog, public catalog product price fields) need updates. Labels: needs-docs, needs-api-spec, module:storefront.

The dummy app never ran: ember-classic-decorator (unused) crashes on Ember 5,
lazy loading kept the engine's modules out of the test bundle, and the host
services, config and intl data the panels need were missing. Mirrors what
fleetops already does: eager loading when `ember test` runs from this package,
an inert stand-in for the console's extensions module, the API config the
core adapter reads at module scope, and the legacy built-in components the
engine's templates still use.

The dummy intl service ignores missing locale data, since headless Chrome has
no number formats for every bundled locale, and the order details and network
controller tests stub the host router, fetch and app cache the way the widget
tests already do.
…rce card

Catalog, truck and network index pages use Layout::Resource::CardsGrid with
one card each on Layout::Resource::Card, following the Fleet-Ops anatomy:
identity and subtitle in the header, media or facts in the body, status,
icon actions and meta in the footer, with registry slots at each end.

A catalog card shows its categories with product counts, its hours as a
seven-day strip (the new Storefront::HoursStrip) and its product total; a
draft is dashed. A truck card is a Vehicle::Pill with the online dot, the
vehicle photo, where it serves, and a presence badge that says serving,
offline, inactive, or online with no catalog. A network card shows the logo on
its backdrop, the online state, the currency and member count.

"Food trucks" reads "Trucks" in the UI; the food-truck model, routes and
permissions keep their names. The catalogs and trucks pages gain the search
the header already offered.
… and crashed createRecord; the store summary is store_summary
…s own sidebar for each

- storefront service tracks a network context derived from the network route
- StoreSelector becomes the context switcher with a Networks group and create-network
- application route loads networks; sidebar rewrites for the network context
- network page is a full page with an identity strip; the list hides while one is open
- new Trucks section for a network backed by FoodTruckFilter::network
- network orders and customers are scoped to the network being viewed, not the active store
- createNewNetwork lives on the storefront service
…enu without Networks

- trigger: context icon, name, STORE/NETWORK label and caret; blue for a store, indigo for a network; invitation badge
- menu: Stores and Networks groups with tiles, currency or member count, a check on the active one, readable in dark mode; footer actions read New store / New network
- store navigation follows the design order (Dashboard, Orders, Products, Catalogs, Customers, Trucks, Promotions, Settings); networks live in the switcher, not the menu
- network page loses the All networks button; leaving a network is the switcher's job
…e sidebar re-renders on a switch; networks report their store count
…d an invite panel

- network index is now an Overview (stat tiles, members with 7-day share, invitations, recent orders); settings move to networks.index.network.settings
- NetworkController gains overview, invitations, resend and revoke; one Invite per recipient carrying category, expiry, message and approval in meta
- OrderFilter/CustomerFilter gain a network filter on meta.storefront_network_id; the network orders and customers routes use it
- Storefront::Network::InvitePanel (by email, shareable link, add a store you manage) replaces the share-network and add-stores modals
- stores page: store identity column via a new store descriptor, All/Online/Offline/Invited tabs, bulk assign category and remove, invitation rows with resend/revoke/invite again
- store and network resource descriptors registered with the shared identity registry; SelectOption::Store wrapper; width-style helper
…ost re-export broke console boot before the lazy engine loaded
…ore stacks and next actions

- a multi-store checkout is one expandable row (customer, stores, fulfillment, total with tip split, shared driver, least-advanced status) that opens into each store's order
- status tabs (needs action, preparing, ready for pickup, out for delivery, completed, canceled) drive the status query param; grouped/flat toggle
- new cells: reference, stores (identity stack of store pills), fulfillment, total, driver (inline assign), status, placed, actions (next action promoted, rest behind an ellipsis)
- order index resource keeps meta.checkout_id so the console can group
…lide with the networks list's page, sort and query
…ive questions first

- header: id, status and store/network badges, the next workflow action promoted to a button, Edit, the rest behind a menu
- Storefront::Order::FlowSteps renders the activity flow as steps (custom order configs contribute their own)
- Storefront::Order::IdentityStrip: customer, driver with inline assign, fulfillment and timing, total with tip and promotion
- tabs: Items (with store block and the other orders of the checkout), Route (with tracking), Payment, Activity (with comments), Customer, Data (metadata and documents); registered tabs still appended
- panel widened to 600px
…out rules and a save bar

- settings shell: store identity header with copy-store-key, a section rail (General, Branding and media, Locations, Checkout rules, Payment gateways, Notification channels, Alerts, API keys); anchors on the General page, routes for the rest
- General page restructured into always-open sections: general, branding and media, checkout rules, alerts
- every rule the server honours is a Storefront::SettingRule row with its consequence written under it, detail inputs appear when it is on
- sticky save bar with unsaved-change count and the sections touched; Discard rolls back attributes, options and alerts
…f categories, products, hours and who serves it

- new full-page route catalogs.index.edit: categories on the left (add, rename, reorder, remove), the selected category's products in the middle with an in-place picker over the store's products that hides what is already in the catalog, health on the right; Hours (ScheduleManager), Served by (stores and trucks) and Details sections; sticky save bar with a summary of unsaved changes
- a store can now serve a catalog like a truck: Store gains catalog relations, PUT catalogs/{id}/subjects syncs stores and trucks, the catalog resource exposes hours and subjects
- category order persists; the catalog list opens the editor, the create modal only asks for name, description and status, and the card shows who serves the catalog
… issue badges and a save bar

- Storefront::Product::Form gains tabs: Details (basics, pricing, flags, a folded More for translations and metadata), Variants and add-ons, Availability, Media (images and videos)
- inline validation: missing name or price, sale price not below price, published without category or image; the tab holding the field carries a badge and the save bar links to it
- status select moves into the panel header; the save bar counts unsaved changes across the product, its variants, add-ons and queued uploads, with Discard
…ns on one screen

- header rebuilt: photo, name, type and preference badges, copyable id, since-date, and a lifetime strip (orders, spend, average, last order) from a new customers/{id}/insights endpoint scoped to the store or network
- actions: New order for this customer, Edit, copy id/email
- tabs: Overview (contact facts, saved places with the default marked, stores ordered from in network context, recent orders), Orders, Places (set default, open in Fleet-Ops from the pill), Activity and notes (first/last order, cancellations, internal notes via CommentThread); registered tabs keep working
- panel widened to 600px
…builder, a redemptions screen and promotion cards

- hub shell: title, subtitle, live counts (active, scheduled, redemptions, discount given) from promotions/hub, one tab strip across promotions, campaigns, segments, pushes and the new Redemptions tab
- promotions list: status tabs (all, active, scheduled, paused, ended, draft), cards or table, Storefront::Promotion::Card with value, trigger, window, targets and redemptions, row actions gain duplicate and view redemptions
- Storefront::Promotion::Builder replaces the modal: What, When it applies (with codes once saved), Targets (products, categories, exclusions, segment), Schedule and limits, Review and announce; done steps collapse to one line; save draft at any step
- Redemptions: PromotionRedemption controller, filter, resource and relations, frontend model, route and table
- Storefront::Promotion::Codes extracted from the codes modal so the builder and the modal share it
… can run their own trucks

- Storefront::Truck::Panel (Details: vehicle, service area, zone, status; Catalogs: assign with serving-now badges) replaces the create/edit and assign-catalogs modals
- trucks page gains All/Online/Offline/Inactive tabs; the network trucks page gets New truck, owned by the network
- food_trucks.network_uuid (nullable) with model relation, resource fields, filter (network: member stores' trucks plus network-owned; ownedByNetwork) and public API ownership rules
- card shows the owning network
… status tabs, bulk actions and draft hints

- category sidebar nests by parent, shows per-category counts from a new products/summary endpoint, and offers smart groups (out of stock, drafts, recommended, on sale, uncategorised)
- status tabs (all, published, draft, on sale) and smart groups drive new product filters: status, category, available, on_sale, recommended, uncategorized
- cards and rows are selectable; a bulk bar publishes, unpublishes, moves to a category, marks on sale or deletes the selection
- a draft card says what it is missing (category, image, price); an out-of-stock card that is still published says so
… sidebar and render the status tabs and bulk bar; the earlier edit never applied and the sidebar crashed on an undefined handler
…n today and deep-links into it

- analytics/attention returns orders to confirm (with the oldest wait), pickups ready, deliveries without a driver, published products out of stock, trucks offline and pending network invitations for the active store
- Widget::AttentionStrip renders them as clickable tiles and is registered as a default storefront dashboard widget
…twork invitation

- the invitation email now lands in the engine at storefront/networks/join/:uri, which showed nothing before
- join page reviews the network (identity, currency, category, approval, message, sender, expiry), picks which store joins, and accepts or declines; closed invitations say why
- endpoints: lookup, accept (creates the membership in the invited category; awaiting_approval when the network asked to review), decline, and pending invitations for a store by its email
- the store dashboard shows a banner per open invitation and the context switcher carries a badge
…own checkout

- orders.index.new is a real form: customer, fulfillment (delivery or pickup, ASAP or scheduled, store location, the customer's saved places), items from the store's published products with inline variant and add-on choices, notes, promotion code, payment (cash on delivery or mark as paid), tip, and a live summary
- prices, delivery fee, discounts, minimum order and totals come from the API: POST orders/console/quote builds a cart, quotes delivery through the service-quote logic and evaluates promotions; POST orders/console/place runs the cash checkout and the same capture that creates app orders
- placing opens the order panel; the customer panel's New order lands here with the customer preselected
…t_items_unavailable error naming them; seeders keep customer ids and their app accounts
…cts only that; Mongolian test customers use emails QPay accepts
… rendered now do

New truck: an icon tile instead of a placeholder photo, a one-line subtitle, no tabs
until the truck exists, uppercase section titles on one gutter, and the save bar pinned
flush to the bottom with no second rule above it. The console ships only the solid and
brand icon sets, so every regular-style icon (truck catalog checkboxes, the catalog
picker's selection, the create-order option checkboxes and radios) rendered as nothing;
they are a drawn .storefront-check now.
… place when a status is empty

The hub loads the owner's promotions once (up to 100) and the tabs filter them in the
controller instead of re-querying behind the loading skeleton; Scheduled is an active
promotion that has not started. An empty status shows one line under the tabs instead
of replacing the page with a no-results state.
…e; the save bar spans the content flush

The ContentPanel heading in settings, customer and order panels drops the darker band
and the rule under it, reads 14px uppercase, and sits on a compact padding. The settings
form fills the content area (sections keep their reading width) so its sticky save bar
runs flush edge to edge instead of floating with the page padding around it.
…order with identity cells, row click and aligned totals
…here, paged and sorted by Storefront; row Edit opens the details form
…s, cards and table, as the server applies it
…ace and hover reaches the sticky ID and action cells
…th a Placed column and the row rhythm of the lists
… its header, tabs, columns, checkbox, cards and rails; the dashboard orders widget gives Placed and Next their width
… list includes the optional KPIs; network customers route reads Storefront's endpoint
…e it; the status steps get room, the customer shows their phone, actions share one compact height; the store section becomes a store profile; section headings compact and aligned with their content, settings sections single cards; Delivery badges in their own colour; breakdown strings translated
…; nested panels align their fields with their heading
…stomer's Places tab and from the new order form; Places has a real empty state; a chosen address reads as a place card with Change address; 20% and 30% tips; order notes full width with room below
…er column, panels, widgets, incoming order) with the id beside it; dashboard widgets use the orders page's identity cells; table row buttons share one 26px height
…card gets a 4:3 image (or a quiet placeholder), a two-line title so prices align, hover and selection on the image, one-height footer buttons; the list pins the product and actions columns; skeletons follow the grid and the bare list
… with thumbnails; variants and add-on groups share one group anatomy (options table, offered count, hidden add-ons); availability as a compact week for products, catalogs and store locations; translations as language chips over the name and description with the originals as hints; the save bar counts every change
… move; card buttons share one height; the catalog editor's actions header no longer overflows
…rebalanced; the products grid fits below its toolbar with room to scroll the last row clear; card footers sit on the card's bottom edge; card buttons share one line
…ut shows round store logos with the summary on hover (the network itself included); the order and actions columns stay put while scrolling; a reference shows its item count instead of the checkout id
…rs, reached from the stores table, the overview cards and a customer's stores; overview cards four across with the store backdrop and logo; switching to a network lands on its overview without loading the networks list, which loads when shown; the overview skeleton also covers returning to the overview; new order store cards match the promotion choice cards; recent order rows show their hover; the category caret sits at the right
… (order id on hover), store and driver as identity cells with a 20px image and the name only, the total without its tip line (tip on hover); a checkout's stores are evenly spaced 20px logos; the child branch follows the view, so Flat shows none
…elation so the save answers with the new catalogs, and clears the cached truck lists since only the pivot changed; the card title opens the truck instead of the Fleet-Ops vehicle panel
…ayment up top, the tracking number with status, fulfillment and timing, the route as stops, customer and driver side by side, the customer's note, the items and totals the order panel uses (now shared as line items, zero tips left out), and the label codes for handover; Accept is green; the truck card opens the truck without a page handler crashing it
Conflicts resolved:
- v1/FoodTruckController: keep network-owned trucks alongside member stores' trucks (superset of release's member-store listing), for both the list and the single-truck lookup
- Resources/FoodTruck: keep the internal 'today' stats next to release's store block
- orders/index/view.hbs: keep the storefront-overlay-body body class
- storefront-engine.css: keep the tuned 0.5rem order panel heading gap
@roncodes
roncodes merged commit 8287f33 into release/v0.4.25 Oct 12, 2026
@roncodes
roncodes deleted the feat/storefront-ux-overhaul branch October 12, 2026 12:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent:in-progress AI agent is actively working on this issue needs-api-spec Requires API specification updates needs-docs Requires documentation updates needs-human-review Requires human review before proceeding

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant