craftivus-manager is a plain React + TypeScript + Vite web app — not a Frappe app itself. It's built as a static bundle and served by the customer's own craftivus_core site. See Architecture: why the manager app is served same-origin for why.
craftivus_core backend (see Installation: Backend) with at least one outlet manager account created — a User with User Type = Website User and the Craftivus Outlet Manager role.git clone <craftivus-manager-repo-url>
cd manager
npm install
npm run dev
vite.config.ts proxies /api and /files to your local bench (default localhost:8000) so that even in dev mode, the app is effectively same-origin — you should never need to fight CORS while developing this app locally.
npm run build
This produces dist/. The deployment step is a direct file copy into the target customer's craftivus_core installation:
cp -r dist/* /path/to/bench/apps/craftivus_core/craftivus_core/public/manager/
cp dist/index.html /path/to/bench/apps/craftivus_core/craftivus_core/www/manager.html
bench --site <site> clear-cache
After this, the app is reachable at https://<customer-site>/manager — served as an ordinary static asset by that customer's own Frappe instance. There is currently no automated deployment pipeline for this step; it's a manual copy per customer per release. Automating it is a known future improvement, not yet built.
Login works exactly like any same-origin web app: POST /api/method/login with the manager's email/password, which sets a normal Frappe session cookie. From then on, every API call (craftivus_core.api.manager.*) is authenticated by that cookie — no api_key/api_secret involved, unlike the till (managers are real people logging in through a login form, not headless devices).
All screens call into craftivus_core.api.manager.*, which is authorized entirely by session + the Craftivus Outlet Manager role check inside _require_manager() — these accounts have no direct Frappe doctype permissions at all (per ADR 0001), so every allowed operation is explicitly coded, not inherited from a Role.
| Screen | What it does |
|---|---|
| Staff | List/create cashiers, set status (a cashier gets a setup_code to redeem on a till) |
| Stock | Receive stock (creates a Purchase Receipt), count stock (creates a Stock Reconciliation) |
| Items & Prices | Create/edit items and their prices on the outlet's price list |
| Sales | Today's (or any date range's) sales summary, by cashier, with recent invoices |
| Tills | Create/suspend/reactivate/revoke POS Device records |
| Shifts | Shift history with cash-reconciliation variance (short/over/exact) flagged |
Not yet built: a UI for receipt-format settings (the craftivus_core.api.manager.get_receipt_settings/save_receipt_settings API already exists and works — there's simply no screen calling it yet), and a subscription-status/expiry banner.
| Symptom | Cause | Fix |
|---|---|---|
| Manager app loads but every API call 403s | Session cookie isn't being sent, or the account lacks Craftivus Outlet Manager |
Confirm credentials: "same-origin" is set on the fetch helper, and check the User's roles in Desk |
| Build succeeds locally but the deployed copy shows a blank page | Stale cached assets on the Frappe site after a redeploy | bench --site <site> clear-cache after every copy |
tsc passes but npm run build fails with erasableSyntaxOnly or a baseUrl error |
TypeScript's stricter production build settings caught something tsc --noEmit alone didn't |
Run the full npm run build before considering a change done — tsc --noEmit during dev is not sufficient proof the production build will succeed |