FloCafe
FloCafe는 구독 없이 무료로 주문·테이블·주방 디스플레이·로컬 데이터를 관리하는 로컬 퍼스트 레스토랑 POS입니다
런던 디자인, 자체 공장에서 만든 개·고양이용 하니스·레인코트·안정 침대·장난감·여행용품, 영국 50파운드 이상 무료배송
Harnesses, raincoats, calming beds, toys and travel gear for dogs and cats. Designed in London, made in our own factory. Free UK delivery over £50.
커머스JavaScript · CSSGitHub @codecrafters-in온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Open-source React e-commerce storefront. Prerendered for SEO with Product JSON-LD, CRO built in, and AI prompts that generate your backend. Runs on bundled demo data or your own API.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A complete, production-shaped e-commerce front end. It runs on a bundled
catalogue out of the box, and on your own backend by changing one environment
variable.
Built by CodeCrafters. MIT licensed — fork it, rebrand
it, ship it.
No database, no API key, no account. The demo store works immediately.README 전문 보기 ↗A complete, production-shaped e-commerce front end. It runs on a bundled catalogue out of the box, and on your own backend by changing one environment variable.
Built by CodeCrafters. MIT licensed — fork it, rebrand it, ship it.
npm install
npm run dev # http://localhost:5173
No database, no API key, no account. The demo store works immediately.
Most storefront themes are either a static template you have to gut before it can hold real data, or a full platform you have to adopt wholesale. This is neither. It is a real storefront — filters, variants, stock, cart, discounts, checkout, orders, addresses — talking to one documented interface with two implementations behind it.
VITE_DATA_SOURCE=mock → src/lib/api/mock.js (bundled catalogue, localStorage)
VITE_DATA_SOURCE=api → src/lib/api/http.js (fetch against your server)
Nothing above src/lib/api/ knows which one is running. Design against the mock,
demo it to a client, then point it at a real backend without touching a
component.
docs/API.md is the whole specification. If your server answers those routes with those shapes, this works.
Alongside the data adapters sits a configuration document (GET /storefront)
that drives identity, currency, navigation, the entire home page,
recommendations and checkout. Between the two, almost everything a merchant
would want to change is data rather than code.
/admin — demo credential admin / admin.
A working back office: a full product record editor (details, media, variant matrix, fit and fabric, categories), inventory, category tree, shared size charts, orders, storefront settings, import and export. It is also the reference implementation of the write API, so every documented endpoint has a screen proving it works.
In mock mode it writes to a local database that the storefront reads, so an edit in admin shows on the shop immediately, with no publish step.
The documentation is served inside it at /admin/docs — the same markdown
files, rendered, with copy buttons on every prompt.
| Mode | Database | Backend |
|---|---|---|
mock | None — browser storage | None |
api | Someone else's | Odoo, Shopify, Medusa, WooCommerce |
api + your own | This schema | You build it, from the prompts |
Every page below is also served by the storefront itself at /docs — same
markdown, no second copy — and it is prerendered, so it is readable without
JavaScript and indexable. /docs/api carries a live explorer: pick an endpoint,
press Run, and read the JSON the adapter actually returns. On a demo that is the
bundled catalogue; pointed at your API it is a real request.
The link sits in the footer's utility bar while the shop runs on demo data and
disappears once it is pointed at a real backend — a shop selling shirts should
not offer its customers an API reference. features.docsLink overrides it
either way.
| docs/README.md | Index — start here |
| CHANGELOG.md | What changed, newest first |
| CONFIGURATION.md | Every setting: menu, home page, currency, features |
| API.md | Endpoint reference |
| DATA-MODEL.md | Every object shape |
| CHECKOUT.md | Payment integration, with Stripe and Razorpay examples |
| CRO.md | The trust and fit elements, and the research behind them |
| THEMING.md | Palette, type, logo, brand assets, image ratios |
| RECIPES.md | Odoo, Shopify, Medusa, WooCommerce mappings |
| ADMIN.md | The back office and the write API |
| PERFORMANCE.md |
| Route | What it is |
|---|---|
/ | Home — hero, categories, new in, editorial, collections, bestsellers |
/shop · /shop/:category | Catalogue with faceted filters, sort and pagination |
/collections/:slug | A curated set, same grid |
/product/:slug | Gallery, colour and size pickers with per-variant stock, fit panel, size chart, fabric and certifications, reviews with fit data, related, sticky buy bar |
/admin/* | Products, inventory, categories, orders, settings, import/export |
/search?q= | Free-text search |
/cart | Full bag, quantity, discount codes, free-shipping progress |
/wishlist | Saved items |
/checkout | Contact, address (with a state list per country), delivery method, payment step, order summary |
/checkout/return | Where a shopper lands after a gateway's hosted payment page |
/order/:id | Confirmation and receipt |
/account | Overview (latest order, details, default address, saved items), orders, address book |
/login | Sign in and register |
/pages/:slug | Size guide, shipping, care, contact — content built into the theme |
* | 404 |
Plus a slide-in cart drawer, a mobile filter sheet, and toasts.
cp .env.example .env.local
| Variable | Default | What it does |
|---|---|---|
VITE_DATA_SOURCE | mock | mock or api |
VITE_API_BASE_URL | — | Required when api. No trailing slash |
VITE_API_TIMEOUT | 12000 | Request timeout in ms |
VITE_API_CACHE | on | off shows backend edits on the next load. For development. Browser only — the build always caches |
VITE_STORE_NAME | LOOM | Wordmark and page titles |
VITE_CURRENCY | USD | ISO 4217 |
VITE_LOCALE | en-US | Number and date formatting |
VITE_FREE_SHIPPING_OVER | 150 | Threshold, in major units |
VITE_REPO_URL | this repo | Footer source link |
Vite inlines every
VITE_*variable into the shipped bundle. Anything here is public. Never put a secret key in one.
Switching to a real backend:
VITE_DATA_SOURCE=api
VITE_API_BASE_URL=https://api.yourstore.com/v1
The app fails at boot with a readable message if api is set without a base
URL, rather than silently making requests to undefined.
Money is integer minor units. { amount: 12800, currency: "USD" } is
$128.00. Never a float — 0.1 + 0.2 !== 0.3, and a cart that adds up in floats
is eventually a cent out on a real invoice. src/lib/money.js handles
formatting and zero-decimal currencies.
The cart is owned by the server. Every mutation returns the whole repriced cart and the client replaces state wholesale. Discounts, shipping thresholds and tax are server concerns; a client that recomputes them will disagree with the invoice sooner or later.
Facets come from the API, over the whole category. Deriving them from the visible results is the classic catalogue bug: filter to one colour and every other colour vanishes from the panel.
Responses are validated at the boundary. A 200 with a missing price throws
a ContractError naming the endpoint and the field. A wrong shape that passes
silently surfaces three components later as a null dereference and costs you an
afternoon.
Checkout stops before payment, deliberately. Card details must never touch a storefront theme — it drags your entire frontend into PCI scope. Hand off to Stripe Elements, Razorpay or Adyen and create the order server-side on the payment webhook.
Filter state lives in the URL. Shareable, bookmarkable, and survives the back
button. None of which you get from useState.
The apparel fields are the point. Size and fit cause roughly two thirds of
fashion returns, and apparel return rates run 20–40% — the highest of any
category. fit, fabric, sizeChart and per-review fit data all ship through
the API for that reason, and the reasoning is written up in
CRO.md with sources.
Requests are generation-guarded. Change a filter twice quickly and the slow first response cannot overwrite the fast second one.
One request for the first screen. GET /bootstrap returns settings,
categories, collections and the home rails together — five sequential round
trips become one. Reads then pass through a cache that de-duplicates in-flight
requests and serves stale-while-revalidating; writes purge only the namespaces
they could have touched. See PERFORMANCE.md.
The demo catalogue is its own chunk. In api mode those 34KB never leave
the server.
Every route sets its own metadata, and product pages emit Product
structured data with real availability and — only when there are reviews — an
aggregate rating.
npm run images fetches every image from Unsplash, crops it to the right
aspect, grades it to a common exposure so a hundred unrelated photos read as
one lookbook, and pins the chosen photo ids in scripts/images.lock.json so the
same commit always builds the same store.
Choosing a photograph is three checks, and each one exists because the catalogue used to fail it:
Chosen ids are unique across the run, so no two products share a photograph.
npm run images # fetch anything missing
npm run images:force # re-roll everything
npm run images -- --only=products/leather-belt # re-roll one slot
npm run images -- --candidates=categories/shirts # what the search offers, as JSON
To overrule the search for one slot, put "id": "<photo id>" in the lockfile
entry and delete that file from public/images/; the next run fetches your
photo and fills in the credit. --candidates is how you find the id.
Credits are written to public/images/CREDITS.md. Replace the whole folder with
real product photography when you have it — the shapes are 4:5 for products and
categories, 3:2 for collections, 16:9 for the hero.
src/
lib/
api/
index.js the switch — asserts both adapters match
mock.js bundled backend
http.js real backend
contracts.js types + boundary validation
config.js every env var, resolved once
money.js minor units, formatting, discounts
data/catalog.js the demo catalogue (mock mode only)
store/ cart, wishlist, auth, toasts
components/ ui/ product/ cart/ layout/ shop/
pages/ one file per route
Re-skinning is one block: the CSS custom properties at the top of
src/index.css. Every component reads tokens, never literals, so changing the
palette changes the whole store.
A live store runs on Odoo 19 with the LOOM Storefront app from the Odoo App Store (search for "LOOM Storefront"
on apps.odoo.com). Odoo is the back office: products, prices, stock, orders, payments, customers, content and emails
stay there, and this storefront is the shop on its own domain. In Odoo, Storefront › Configuration › Set up a store
gives the values to put in VITE_API_BASE_URL, SITE_URL and LOOM_WEBHOOK_SECRET. The app needs Odoo.sh or your own
Odoo server; it cannot be installed on Odoo Online.
npm run build # → dist/ (files) and server-build/ (the render handler's bundle)
npm start # the Node server: dist/ plus pages rendered on request
A live store renders each page when it is asked for (server/handler.mjs), so a product added in Odoo after the
build is a real page for crawlers: its own title, description, Open Graph tags and structured data, a 404 for an
address that is nothing, a 301 for a changed slug, and robots.txt and the sitemap from Odoo. Adapters are in the
repository for Vercel (vercel.json, api/render.js), Netlify (netlify.toml,
netlify/functions/render.mjs), Cloudflare Pages (functions/[[path]].js) and any Node host or Docker
(server/node.mjs, Dockerfile). Step by step, with the environment variables: docs/DEPLOY.md.
The demo is written out as files at build time and runs on any static host. Set the site URL, or its canonical tags
point at a placeholder: SITE_URL=https://yourshop.com npm run build (Vercel's VERCEL_PROJECT_PRODUCTION_URL is
picked up on its own). vercel.json sets cleanUrls so /product/x finds product/x.html; public/_redirects does
the same on Netlify and Cloudflare Pages; with nginx, try_files $uri $uri.html $uri/ /index.html;.
Do not add a catch-all rewrite to
/above those. It serves the home page for every address. It looks correct in a browser, which routes around it, and is invisible until you check what a crawler actually received.
MIT. No attribution required, though a link back is appreciated.
| Bootstrap, caching, pagination, webhooks, scale |
| USER-GUIDE.md | For whoever runs the shop — ten tasks, one screen each |
| DATABASE.md | PostgreSQL schema for running your own backend |
| ERRORS.md | Error codes and a debugging checklist |
| TESTING.md | The suite, what it covers, and what it deliberately does not |
| INTEGRATION-PROMPT.md | Copy-paste prompt that has an AI build your backend |
| SCHEMA-PROMPT.md | Copy-paste prompt that has an AI design your database |