Skip to content

Feat/kds improvement - #549

Merged
mnindrazaka merged 21 commits into
mainfrom
feat/kds-improvement
Sep 18, 2026
Merged

mnindrazaka merged 21 commits into
mainfrom
feat/kds-improvement

Conversation

@mnindrazaka

Copy link
Copy Markdown
Collaborator

No description provided.

mnindrazaka and others added 17 commits September 18, 2026 12:36
Baristas and customers previously saw only "order is ready" with no
item list, so there was no way to verify order contents at pickup or
review them from order history. Extract the item-list rendering
(already used by OrderPreparingView) into a shared OrderItemsSummary
component and reuse it in OrderReadyView; the payment entity already
carries items end-to-end, so no usecase/API changes are needed.


Claude-Session: https://claude.ai/code/session_015LLRrnUHRWWJgtAZA4j6ic

Co-authored-by: Claude <noreply@anthropic.com>
…rt (#535)

Selecting a product while the grid was mid-revalidate or mid-param-change
silently dropped the click, leaving the picker on the previous product's
amount. SELECT_PRODUCT now also resets amount to 1 from those states, not
just 'loaded'.

Also add a soft stock check: the amount picked before pressing Submit can
be stale by the time the fresh variant comes back (another cashier may
have sold the last ones in the meantime). FETCH_VARIANT_SUCCESS now caps
the amount to the freshly fetched sellableQuantity, or bounces back to the
picker if the variant just went sold out, instead of blindly adding the
stale amount to the cart.


Claude-Session: https://claude.ai/code/session_016oePpwRYKhb8cuhcu6V8pc

Co-authored-by: Claude <noreply@anthropic.com>
…#536)

The Expo push payload omitted `priority`, so Expo defaulted to
`priority: 'default'` — FCM `normal` on Android, `apns-priority: 5` on
iOS. Both platforms treat that as deliverable whenever convenient:
Android holds normal-priority messages for the duration of Doze and iOS
batches priority-5 pushes to conserve power. A barista locking the phone
therefore got nothing, and every held order arrived at once the moment
the screen came back on.

Expo delivers to FCM as a data message that `expo-notifications`' own
service turns into the visible notification, so waking the app is a
precondition for the notification existing at all — and a
normal-priority data message in Doze wakes nothing.

`KdsPushPriorityHigh` is set in the domain next to `Sound` and
`ChannelId`, and used by both construction sites: the dispatched order
and the setup screen's test notification, which would otherwise behave
differently from the real thing.


Claude-Session: https://claude.ai/code/session_01AhcJGVXhTqVvjHnJhqgHQ8

Co-authored-by: Claude <noreply@anthropic.com>
* docs: add PRD for guest web push notifications in order-web

The order app tells a guest their order is ready only by short polling
from an open tab (OrderStatusUsecase's 10s interval), so a guest who
locks their phone or switches apps learns nothing. The staff side of the
same gap was closed by docs/prd-kds-order-notifications.md; this is the
guest-facing half.

Specifies Web Push with VAPID signed by apps/api, a hand-written service
worker in apps/order-web, subscriptions keyed to the existing
gl_session_id guest identity, and a guest_notifications outbox enqueued
inside CompleteTransaction. Covers the new tables, API contract, backend
and libs/ui file layout, and the iOS Home-Screen constraint that decides
most of the UX. Broken into nine phases, one PR each.

No code changes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014ErZwY6wGvnRSiAUYbDKWY

* docs: add phase dependency graph to web push PRD

The numbered phase list read as a strict sequence when three of the nine
phases depend on nothing. Adds a Phase dependencies section: a per-phase
table separating hard dependencies (does not compile or test without the
other) from soft ones (merge order and acceptance only), plus the six
parallel waves the graph collapses into and the six-phase critical path.

Splits the transport types into domain/web_push_gateway_repository.go so
phases 1 and 2 share no symbol and can be built at the same time (D17),
recorded as a deliberate departure from the convention both existing
gateway interfaces follow, with the cost of reverting it stated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014ErZwY6wGvnRSiAUYbDKWY

---------

Co-authored-by: Claude <noreply@anthropic.com>
)

Phase 1 of docs/prd-order-web-push-notifications.md: the guest-facing
subscription slice for order-ready web push. Adds the
web_push_subscriptions table, WebPushSubscriptionRepository with its
MySQL implementation and mock, WebPushSubscriptionUsecase (subscribe
upserting on endpoint, unsubscribe soft-deleting, list by session,
config), the three contract schemas/operations, the
web-push-subscription handler/route wrapped in RequireSessionId, the
public VAPID config route, and the WEB_PUSH_VAPID_PUBLIC_KEY /
WEB_PUSH_VAPID_PRIVATE_KEY / WEB_PUSH_SUBJECT env vars. Nothing sends
a push yet — that lands in later phases.


Claude-Session: https://claude.ai/code/session_017QR4LhgGxxLcyq7GYUUumC

Co-authored-by: Claude <noreply@anthropic.com>
…539)

Phase 6 of docs/prd-order-web-push-notifications.md. Ships the inert
static prerequisites for guest web push: sw.js handles push and
notificationclick, manifest.webmanifest enables standalone install
(required for iOS Web Push per D12), icons derived from the venue
logo, and next.config.js serves sw.js/the manifest with
Cache-Control: no-cache so an updated worker isn't stuck behind
Vercel's default caching. No registration code yet — phase 7 wires
the browser repository that calls navigator.serviceWorker.register.


Claude-Session: https://claude.ai/code/session_01J7xY2DeP62zXsQB7XJjLAu

Co-authored-by: Claude <noreply@anthropic.com>
Implements phase 2 of docs/prd-order-web-push-notifications.md:
WebPushGatewayRepository, sending VAPID-signed, RFC 8291-encrypted
notifications via github.com/SherClockHolmes/webpush-go, mirroring
data/expopush's shape. A 404/410 from the push service maps to
WebPushErrorCodeGone so the dispatcher (phase 5) can prune the dead
subscription; every other failure is transient and left for a retry.

Adds the three WEB_PUSH_* VAPID env vars, since phase 1 (which would
otherwise declare the public key) hasn't landed yet.


Claude-Session: https://claude.ai/code/session_01RuApWSajokdXCzxhje9RtX

Co-authored-by: Claude <noreply@anthropic.com>
Phase 3 of docs/prd-order-web-push-notifications.md: the guest_notifications
table, GuestNotification/GuestNotificationStatus, the pure
BuildGuestPushMessage message rule (FR-5), the GuestNotificationRepository
with its MySQL implementation and generated mock, and
PaymentRepository.GetPaymentByTransactionId, which FR-2's session resolution
needs and did not exist yet. Nothing calls any of it yet — wiring lands in
phase 4.


Claude-Session: https://claude.ai/code/session_018uc2bsM4Qf2h2pidvQCpCE

Co-authored-by: Claude <noreply@anthropic.com>
Phase 7 of docs/prd-order-web-push-notifications.md: the WebPushSubscription/
WebPushConfig entities, the browser-side (WebPushRepository) and API-side
(WebPushSubscriptionRepository) interfaces, their API and browser
implementations, mock repositories, and the OrderNotificationSubscribeUsecase
FSM per FR-6. No UI wiring yet — that's phase 8.


Claude-Session: https://claude.ai/code/session_01RnkFJUWC9TZdgwzLCWn65g

Co-authored-by: Claude <noreply@anthropic.com>
…letion (#544)

Phase 4 of docs/prd-order-web-push-notifications.md. CompleteTransaction
resolves the guest's session from the payment row and enqueues a
guest_notifications row after the completion write succeeds, tolerating a
missing payment as a skipped row rather than failing the completion (FR-2).
UncompleteTransaction deletes the outbox row so a correction doesn't
suppress the next real completion (D7). Nothing dispatches these rows yet.


Claude-Session: https://claude.ai/code/session_015WHemhoFz8dJ5KPodVa6G2

Co-authored-by: Claude <noreply@anthropic.com>
… 5) (#545)

GuestNotificationUsecase.DispatchPending claims pending outbox rows and
delivers each to every live subscription for its session, pruning
subscriptions the push service reports gone (410/404) and retrying
everything else up to the existing attempt ceiling. CompleteTransaction
kicks a dispatch after its commit, and the sweeper (renamed
runNotificationSweeper) now drives both the KDS and guest outboxes from
one ticker per docs/prd-order-web-push-notifications.md D5.


Claude-Session: https://claude.ai/code/session_01DFUNkiPqmE1cdMm9YfbDBs

Co-authored-by: Claude <noreply@anthropic.com>
Adds OrderNotificationOptIn, rendered by OrderPreparingView and driven by
OrderStatusHandler's second use case (OrderNotificationSubscribeUsecase).
The composition root in app/order/OrderStatus.tsx now wires up
ServiceWorkerWebPushRepository and ApiWebPushSubscriptionRepository, so a
guest can tap "Beri tahu saya" and actually receive a push once phase 5's
dispatcher fires — the first phase where the feature is guest-visible.


Claude-Session: https://claude.ai/code/session_01JV1mYWtZpUB58Y28SLU3dy

Co-authored-by: Claude <noreply@anthropic.com>
…548)

Adds the placeholder chime as the KDS push sound per D23: wires it through
the expo-notifications config plugin's sounds array, bumps the Android
channel id to orders-v2 (a channel's sound is immutable after creation),
and points KDS_PUSH_SOUND at the same filename so the API-built payload
and the bundled asset agree.


Claude-Session: https://claude.ai/code/session_016CkPhzZeN5QCniQDuCUfmt

Co-authored-by: Claude <noreply@anthropic.com>
… (phase 9) (#547)

Closes out docs/prd-order-web-push-notifications.md: a docs-site page
explaining the feature to an operator (including the iOS Home Screen
requirement and what to check when a guest reports nothing arrived), the
three VAPID env vars documented in .env.example and README.md with a
runnable key-generation command (apps/api/cmd/generatevapidkeys, mirroring
the existing cmd/dokucheck and cmd/dokustub tools), and an order-web-e2e
spec that drives a real browser subscription from the preparing screen and
confirms a web_push_subscriptions row exists for the guest's session.

Note: .github/workflows/e2e-main.yml also needs a throwaway VAPID keypair
generated for the API step (the same way it already does for the DOKU
signing key) so this spec has a real key to subscribe against post-merge —
not included here because this token lacks the `workflow` OAuth scope to
push workflow file changes. See the PR description for the exact diff to
apply by hand.


Claude-Session: https://claude.ai/code/session_011j7au7oUXbhNzcxh5ijis2

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Sep 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
gatherloop-pos Ready Ready Preview Sep 18, 2026 11:51am UTC
gatherloop-ui-storybook Ready Ready Preview Sep 18, 2026 11:51am UTC

@vercel

vercel Bot commented Sep 18, 2026

Copy link
Copy Markdown

Deployment failed for project gatherloop-ui-storybook with the following error:

Resource is limited - try again in 24 hours (more than 100, code: "api-deployments-free-per-day").

Learn More: https://vercel.com/m-nindra-zakas-projects-ae6b8849?upgradeToPro=build-rate-limit

@vercel

vercel Bot commented Sep 18, 2026

Copy link
Copy Markdown

Deployment failed for project gatherloop-order with the following error:

Resource is limited - try again in 24 hours (more than 100, code: "api-deployments-free-per-day").

Learn More: https://vercel.com/m-nindra-zakas-projects-ae6b8849?upgradeToPro=build-rate-limit

@vercel

vercel Bot commented Sep 18, 2026

Copy link
Copy Markdown

Deployment failed for project gatherloop-pos with the following error:

Resource is limited - try again in 24 hours (more than 100, code: "api-deployments-free-per-day").

Learn More: https://vercel.com/m-nindra-zakas-projects-ae6b8849?upgradeToPro=build-rate-limit

mnindrazaka and others added 2 commits September 18, 2026 18:31
The jest mock for @tamagui/lucide-icons was missing CheckCircle2,
so OrderReadyView rendered undefined and failed OrderStatusHandler's
"already ready" test.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YLHEzYqXJTqXbpRQZDMQUL
@mnindrazaka
mnindrazaka merged commit 6891e51 into main Sep 18, 2026
5 of 7 checks passed
@mnindrazaka
mnindrazaka deleted the feat/kds-improvement branch September 18, 2026 12:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants