尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證POS for Gatherloop Cafe
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
A Point of Sale system built for a coffee shop, running on web, mobile and a customer-facing
ordering app. It covers the catalog (categories, products, variants, materials), sales
(transactions, carts, tables, tickets, coupons), inventory (suppliers, stock checks), finance
(expenses, budgets, wallets, profit calculation) and daily operations (checklists, rentals).
The full feature documentation lives at
gatherloop.github.io/gatherloop-pos (source in
docs-site/).
An Nx monorepo. Apps are thin shells; nearly all frontend code lives in
libs/ui, shared by web, mobile and order.檢視完整 README ↗A Point of Sale system built for a coffee shop, running on web, mobile and a customer-facing ordering app. It covers the catalog (categories, products, variants, materials), sales (transactions, carts, tables, tickets, coupons), inventory (suppliers, stock checks), finance (expenses, budgets, wallets, profit calculation) and daily operations (checklists, rentals).
The full feature documentation lives at
gatherloop.github.io/gatherloop-pos (source in
docs-site/).
An Nx monorepo. Apps are thin shells; nearly all frontend code lives in
libs/ui, shared by web, mobile and order.
apps/
api/ Go backend (REST API, MySQL)
pos-web/ Next.js admin/cashier app (Pages Router)
order-web/ Next.js customer app — scan a table QR, order and pay by QRIS from your phone
pos-mobile/ React Native (Expo) app for iOS/Android
kds-mobile/ React Native (Expo) app — a dedicated phone that buzzes when an order is paid
*-e2e/ Playwright end-to-end tests per app
libs/
ui/ All shared frontend code: entities, use cases, screens, components
src/app/ per-app composition roots — pos/ and order/
src/presentation/ handlers/{pos,order,hooks}, views/components/ (shared), views/screens/pos/ and views/screens/order/
api-contract/ OpenAPI spec (src/api.yaml) + generated TS and Go clients
provider/ App-level providers (Tamagui, theme, toast)
docs/ PRDs and TRDs (product and technical design docs)
docs-site/ VitePress feature documentation site
@gatherloop-pos/ui exports the shared domain/data/utils layer; @gatherloop-pos/ui/pos and
@gatherloop-pos/ui/order each export only their own app's composition roots, so a POS surface
cannot accidentally import the customer app's screens (or vice versa) — see
docs/trd-ui-presentation-split-by-app.md.
The three frontends share the same UI layer through Tamagui, which renders to both React DOM and React Native.
npm install
cp apps/api/.env.example apps/api/.env # DB credentials, JWT secret, CORS origins
cp apps/pos-web/.env.example apps/pos-web/.env.local
cp apps/order-web/.env.example apps/order-web/.env.local
cp apps/pos-mobile/.env.example apps/pos-mobile/.env
cp apps/kds-mobile/.env.example apps/kds-mobile/.env
apps/api's checkout endpoint validates ORDER_PAYMENT_WALLET_ID on each request and returns an
error if it is unset, unknown, deleted, or not a payment target (docs/prd-order-checkout-qris-doku.md,
D15) — before trying checkout locally, create a wallet (POS → Wallets) and point apps/api/.env at
its id.
apps/api/.env's EXPO_PUSH_ACCESS_TOKEN, KDS_PUSH_SOUND and KDS_DISPATCH_INTERVAL_SECONDS
configure push delivery to apps/kds-mobile (docs/prd-kds-order-notifications.md) — the app
still registers and boots without a real Expo token, but no notification is actually delivered
until one is set.
apps/api/.env's CASH_PAYMENT_EXPIRY_SECONDS (default 600) is the walk-and-queue window an
order-app guest gets to pay cash at the till before the order is cancelled automatically
(docs/prd-order-cash-payment.md, D5). apps/order-web/.env.local's
NEXT_PUBLIC_ORDER_CASH_PAYMENT_ENABLED is the method's own kill switch, off by default —
NEXT_PUBLIC_ORDER_CHECKOUT_ENABLED must also be true for the cart's pay button to be reachable
at all; NEXT_PUBLIC_ORDER_CASHIER_LOCATION (default Lantai 1) is the till location shown to the
guest in both the checkout sheet and the cash instruction screen.
apps/api/.env's ORDER_COD_PAYMENT_ENABLED (default false) is the server-side gate for the
Cash on Delivery trial — the authoritative one, since it is what stops an old tab, a curl, or a
cached bundle from creating a COD order (and the verification photo that comes with it) once the
trial is off (docs/prd-order-cod-payment.md, D12). COD_VERIFICATION_EXPIRY_SECONDS (default
900) is how long an unverified COD order waits for a barista before the sweeper cancels it and
frees the guest's cart. apps/order-web/.env.local's NEXT_PUBLIC_ORDER_COD_PAYMENT_ENABLED is
the method's own client-side kill switch, off by default and only ever an affordance — the API flag
above is what actually matters. Migration 000044_add_payment_verification must be applied
(MIGRATIONS_DIR=data/mysql/migrations make migrate-up from apps/api) before the API binary that
expects the payment_verification_photos table starts. In production, turning the trial on is
ORDER_COD_PAYMENT_ENABLED=true on the API and NEXT_PUBLIC_ORDER_COD_PAYMENT_ENABLED=true on
order-web, alongside NEXT_PUBLIC_ORDER_CASH_PAYMENT_ENABLED=false — COD is a superset of cash
from the guest's point of view, so the two trial flags are meant to be flipped together.
apps/api/.env's FONNTE_TOKEN, FONNTE_BASE_URL and ORDER_WEB_BASE_URL configure the
WhatsApp order-ready notification sent when a barista marks an order-app order ready
(docs/prd-order-whatsapp-notifications.md) — leaving FONNTE_TOKEN or ORDER_WEB_BASE_URL
empty (the default) boots the API with a disabled gateway that records every guest notification
skipped rather than failing checkout or the dispatcher, which is the expected state for local
dev, CI and the e2e stack. go run ./cmd/fonntecheck -to 0812… from apps/api sends one real
message through a configured gateway and prints Fonnte's raw response.
npx nx run api:serve # Go API, on the PORT set in apps/api/.env
npx nx run pos-web:dev # POS web app → http://localhost:3000
npx nx run order-web:dev # customer app → http://localhost:3000
npx nx run pos-mobile:start # React Native dev server (then run-android / run-ios)
npx nx run kds-mobile:start # React Native dev server (then run-android / run-ios)
npx nx run ui:storybook # component explorer → http://localhost:6006
Web and order proxy /api/* to NEXT_PUBLIC_API_BASE_URL, so start the API first. Both default to
port 3000 — to run them side by side, give one another port (npx nx run order-web:dev --port=3001) and
add that origin to CORS_ALLOWED_ORIGINS in apps/api/.env.
npm test # all unit tests (Jest for TS, go test for the API)
npm run lint
npx nx run pos-web-e2e:e2e # Playwright end-to-end tests
npx nx run order-web-e2e:e2e # Playwright end-to-end tests (order app)
npx nx run api-contract:generate:ts # regenerate TS client after editing src/api.yaml
npx nx run api-contract:generate:go # regenerate Go models
Codegen runs automatically as a dependency of the dev, build and serve targets — run it
manually only when you want to inspect the output.
In CI, a pull request runs the libs/ui and apps/api unit tests, and only the ones whose area it
touches (.github/workflows/pr-tests.yml). The end-to-end suites are too slow for that loop, so
they run after the merge, against a MySQL service, the real API binary and a real Next.js server
(.github/workflows/e2e-main.yml).
Both sides follow the same Clean Architecture split: domain → data → presentation, where the domain depends on nothing and the outer layers depend inwards through interfaces.
apps/api)domain/ <entity|repository|usecase>.go per feature
data/mysql/, data/mock/ repository implementations
presentation/restapi/ handler + route + transformer per feature
main.go wires repositories → use cases → handlers
gorilla/mux; transformers map between JSON request
and response shapes and domain entities.Dependencies are constructed once in main.go and injected, so use cases are testable in isolation
(see the *_usecase_test.go files next to them).
libs/ui/src)domain/ entities, repository interfaces, use cases (framework-agnostic)
data/ api/ (OpenAPI client), mock/, memory/, browser/ repository implementations
presentation/ handlers/ (+ handlers/hooks/), views/ (screens/, components/)
app/ per-route composition: builds repositories + use cases, renders a Handler
idle, loading,
loaded, error, …), an action union, and a pure reducer. Contains no React.useUsecase binds a use case's state machine to React (useReducer +
effects); a *Handler calls it directly (or a shared hook in handlers/hooks/ when ≥2 handlers
need the same one), maps that state to props with ts-pattern; a *Screen is pure Tamagui JSX
with Storybook stories.app/pos/ and app/order/; pages in apps/pos-web, apps/order-web and
apps/pos-mobile mostly just re-export these.Every surface builds with the React Compiler, so don't
hand-write useMemo/useCallback/React.memo for re-render performance — opt a misbehaving
component out with "use no memo" instead. (One exception: useCallback around a useFocusEffect
callback is still needed, because the Jest setup has no compiler pass.) See
docs/trd-react-compiler-adoption.md.
docs/ — PRDs and TRDs for each feature; read the relevant one before changing behaviour.docs/forms.md — form conventions (react-hook-form + zod).docs/trd-vps-deployment-automation.md — how the API ships: a static binary built in CI and run
on a VPS under systemd (.github/workflows/deploy-api.yml). The order app deploys to its own
Vercel project (apps/order-web/vercel.json).docs/trd-storybook-vercel-deployment.md — how the component explorer ships: its own Vercel
project rooted at libs/ui (libs/ui/vercel.json).