Saleor Store
우리 컬렉션을 쇼핑하세요
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기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).