# EatMe — Attendance Internal staff system for the EatMe bistro. Employees sign in with Google, log clock-in/out and breaks, sign up for shifts, and confirm their month at the end of it; the admin (owner) manages the employee list and their hourly rates, plans shifts, approves closures, and runs payroll (tips, bonuses, CSV export). ``` backend/ Express + TypeScript API, SQLite (better-sqlite3), jest tests frontend/ React + TypeScript PWA (Vite), Google Sign-In, Zustand docs/ OKF (Open Knowledge Format) bundle — architecture, data model, ops gscript/ Original Google Apps Script version (reference only, not part of the running app) branding/ Logo and generated favicons/PWA icons from the bistro's artwork deploy/ Config templates for hosts outside this repo (nginx server block) Dockerfile Multi-stage build — one image for API and frontend compose.yaml Production deployment of the built image (see Production below) ``` The look is dark, modern, sharp-edged (no rounded corners), with a single amber accent used sparingly — no colored accent borders. Details in [`docs/frontend/design-system.md`](docs/frontend/design-system.md). ## Requirements - Node.js 22+ - A Google Cloud project with an OAuth Client ID configured (Web application) — authorized origins must include `http://localhost:5174` for dev and the production domain. The client ID goes into `GOOGLE_CLIENT_ID` (backend) and `VITE_GOOGLE_CLIENT_ID` (frontend) — they must be **identical**. ## Development ```bash # backend cd backend cp .env.example .env # fill in GOOGLE_CLIENT_ID, ADMIN_EMAILS, JWT_SECRET npm install npm run dev # http://localhost:4000 # frontend (in a second terminal) cd frontend cp .env.example .env # VITE_GOOGLE_CLIENT_ID same as backend npm install npm run dev # http://localhost:5174 (port is pinned — must match the Google origin) ``` The frontend dev server proxies `/api/*` to the backend (see `frontend/vite.config.ts`). ### Typecheck / build / test ```bash cd backend && npm run typecheck && npm test && npm run build # -> dist/index.js (esbuild bundle) cd frontend && npm run build # -> dist/ (Vite, PWA) ``` ## Production (Docker) One multi-stage `Dockerfile` at the repo root builds both frontend and backend and runs them as a single container — the backend serves the API on `/api/*` and the statically-built frontend on everything else. The image is built locally (for the target server's platform) and shipped to the server already built, never as source: ```bash # build for the server's platform (on an Apple Silicon Mac targeting an # x86_64 server, --platform linux/amd64 is required or the image won't # run there at all) docker build --platform linux/amd64 \ --build-arg VITE_GOOGLE_CLIENT_ID= \ -t eatme:latest . # ship the built image over ssh (no build on the server) docker save eatme:latest | gzip | ssh "gunzip | docker load" # send just compose.yaml + .env (see .env.example) and start it scp compose.yaml :~/eatme/compose.yaml ssh "cd ~/eatme && docker compose up -d" ``` `VITE_GOOGLE_CLIENT_ID` has to go in as a **build arg** (Vite bakes it in at build time, not runtime — `.env` on the server only handles runtime vars). The named volume `eatme_data` (see `compose.yaml`) keeps the SQLite database outside the container so it survives a redeploy. A TLS-terminating reverse proxy belongs in front of the container (outside this repo) — an nginx server block template lives at `deploy/eatme.mipem.co`, proxying to `http://:8092`. ## Data model / operations Employees are an email whitelist managed by the admin (`ADMIN_EMAILS` env var — a separate role, not a row in the employees table). Removing an employee is a soft delete (`active = 0`) — attendance history stays, and re-adding the same email reactivates the account. Employees also have a versioned hourly rate, sign up for shifts from a weekly template, and confirm each calendar month before the admin locks it and runs payroll. More in `docs/` (OKF bundle).