ReviewOS

stacks/smakelo

B3.1: Consumer app UI

#12
Open chris opened this 8 days ago ·
8 days ago

Part of #1. Aesthetic: stacks-design-soft; mobile-web first-class; i18n en/de/nl; demo banner; full design pre-flight.

Pages: home/discover, search results (list + ts-maps cluster map with clickable pins), business page (listing + reviews + menu + order in one), cart/checkout (Stripe payment element, sandbox badge), live order-tracking map (courier marker via websocket frames, rAF interpolation, heading DivIcon, OSRM route polyline), orders + order detail, account/addresses/favorites, review composer (receipt-QR deep link entry), CSA signup flow.

8 days ago

Two surfaces done in 9bbe173 and 7b809c0, live now:

  • Discover (/): clustered ts-maps map on OpenStreetMap tiles, cards sorted by distance, live open/closed state, ratings, price tier. Server-rendered, so the list is complete before any script runs and the map is an enhancement.
  • Place page (/places/<slug>, 35 of them): hours table, location map, and either a menu or, for a real business nobody signed up, a plain explanation of why there is nothing to order.
  • Plus /about and /partner, so the nav does not point at 404s.

Remaining here: search and filter controls, cart and checkout (#7), order tracking (#8), account/orders/addresses/favorites (#5), CSA signup (#10).

Note for whoever picks this up: place pages are generated, one render-once view per business over a shared partial, because a per-request stx view silently drops awaited values and imported-function results (stacksjs/stacks#2390). Run buddy build:places after seeding.

8 days ago

Substantial progress, all live.

Discover now has controls. Search, a type filter, open-now and takes-orders toggles, three sort orders. Filtering hides cards rather than re-querying: the whole list is already on the page, so it is instant, there is no second definition of "open now" to drift from the server's, and with JavaScript off no filter bar appears and every result is still there. Map pins hide with their cards. The type row is built from the results themselves, because a hardcoded row of three would have stranded the bakeries, bars and grocers in the seed.

Orders and saved places. Orders are linked to the browser that placed them, so /orders can show them instead of making a guest keep the tracking link. /saved came with #5. The visitor token is read from the request header, never the body, so a client cannot claim to be ordering as somebody else.

Checkout now has a payment step, which it did not before: orders were placed and nothing was ever asked for. It goes through @stacksjs/payments, creates a PaymentIntent against the order's stored total, records it in the framework's payments table, and settles by asking Stripe rather than believing the browser.

⚠️ Blocked on one thing only: there are no Stripe keys in .env. The checkout currently says so plainly rather than showing a card form that could not work. Setting STRIPE_SECRET_KEY and FRONTEND_STRIPE_PUBLIC_KEY to test keys makes the card form appear with no code changes. assessKeys refuses a live key outright and has five tests, because every other line of that file would work fine against a live account.

Still open here: an address book, and the CSA signup flow (tracked separately in #10).

8 days ago

A real gap closed: checkout never asked for a delivery address. The API accepted one and the pricing used it, but nothing asked — so every delivery placed through the site was quoted at the base rate and reached the kitchen with an empty address line. The simulator hid it, because it wrote its own addresses.

There is an address book now. The first address saved becomes the default, since somebody with one address should never have to choose it, and the field appears only for delivery: an address input on a pickup order is a question with no reason behind it.

The address travels with the quote as well as the order. Quoting without it and charging with it would show a total that changes at the last step, which is the one moment a customer is actually reading the number. Verified: an order to 1453 4th St now carries the address and coordinates and prices delivery at $3.11 rather than the flat base.

No geocoder, deliberately. Adding one would mean sending a stranger's home address to a third party so a demonstration could price a delivery nobody will make. An unplaced address sits at the market centre and the fee comes out at the base rate: visibly flat rather than falsely precise.

One modelling note for #3: schema.float() rather than number() for the coordinates. The latter generates an INTEGER column, and a latitude rounded to a whole degree is sixty miles out.

That is everything on this issue except Stripe test keys, which are yours to create — the checkout says plainly that no provider is configured and needs no code change once they exist.

Sign in to comment on this issue.