Both services running, seed is idempotent. Passo 14 completo. O que foi feito: O README foi completamente reescrito para MAI CALL v0.1. Cobertura: Quick-start em <15 min — 6 passos ordenados com comentários inline; a partir de git clone, nenhum pré-requisito adicional além do que está documentado Demo flow — fluxo como operador (picker → novo pedido → offline test) e como admin (queue → claim → resolve) MinIO — endpoint, console (localhost:9001), credenciais, bucket, convenção de chaves, como fazer backup com mc mirror Limitações conhecidas v0.1 — tabela com 10 itens: autologin, sem auth real, sem TAG/RFID, sem SLAs, sem push, Safari Background Sync, sem observabilidade, sem i18n Aviso em destaque — AUTH_DEV_AUTOLOGIN=true é uma back door, nunca usar em produção Notas de arquitectura — multi-tenancy, offline sync, storage Troubleshooting actualizado** — inclui MinIO photos not loading
FieldOps — MAI CALL v0.1
Modular industrial SaaS monorepo. MAI CALL v0.1 is the first shipped feature: a full maintenance-request loop that runs on factory Wi-Fi (and survives losing it).
What's here
apps/
operator-pwa/ Next.js 15 — operator mobile console (port 3000)
admin-web/ Next.js 15 — maintenance queue for admin/supervisor (port 3001)
packages/
db/ Prisma 6 schema + multi-tenant extension
api/ tRPC v11 routers (maintenanceRequest, workstation, user, storage)
storage/ ObjectStorage abstraction + MinIO/S3 implementation
ui/ Shared shadcn-style components
domain/ Pure domain logic (reserved for later modules)
config/ TypeScript / ESLint / Tailwind presets
e2e/ Playwright happy-path test
Prerequisites
| Tool | Version | Notes |
|---|---|---|
| Node.js | 22 LTS | Required by Next.js 15 |
| pnpm | 11+ | npm i -g pnpm@11 or corepack enable pnpm |
| Docker Desktop | any recent | Provides Postgres 16 + MinIO |
| Git | any | — |
Quick-start (< 15 min)
Run these commands from the repo root in order.
# 1. Copy and configure the environment file
cp .env.example .env
# The defaults work out of the box for local dev.
# AUTH_DEV_AUTOLOGIN is already "true" in .env — leave it.
# 2. Install dependencies
pnpm install
# 3. Start Postgres + MinIO (creates the fieldops bucket automatically)
docker compose up -d
# 4. Apply the database schema
pnpm db:migrate
# 5. Seed the demo tenant, 3 operators, and 3 workstations
pnpm db:seed
# 6. Start both apps
pnpm --filter @repo/operator-pwa dev &
pnpm --filter @repo/admin-web dev
| URL | What it is |
|---|---|
| http://localhost:3000 | Operator PWA |
| http://localhost:3001 | Admin / Manutenção queue |
| http://localhost:9001 | MinIO console (see credentials below) |
Demo flow
As an operator (port 3000)
- Open http://localhost:3000.
WithAUTH_DEV_AUTOLOGIN=trueyou land on the home page asadmin@demo.local. To simulate a real operator, navigate to http://localhost:3000/select-operator and tap op1@demo.local. - Tap Pedir manutenção.
- Select a workstation, optionally attach a photo, write a description, and tap Enviar pedido.
- The page shows "Pedido enviado" once the sync completes (usually within 1–2 seconds when online).
Offline test:
Chrome DevTools → Network → Offline → create 3 requests → Network → Online.
The requests sync automatically within ~10 s; "Tudo sincronizado" appears.
As admin / maintenance supervisor (port 3001)
- Open http://localhost:3001 — it lands on the maintenance queue.
- The queue refreshes every 5 s; new requests appear automatically.
- Click Aceitar to claim a request (status: Em curso).
- Click Marcar resolvido, optionally add a note, click Confirmar (status: Resolvido).
- The document tab title shows
(N) FieldOps — Manutençãowhen there are open requests.
MinIO (photo storage)
| Setting | Value |
|---|---|
| API endpoint | http://localhost:9000 |
| Web console | http://localhost:9001 |
| Root user | fieldops |
| Root password | fieldops123 |
| Bucket | fieldops |
Photos are stored as presigned-URL objects under
tenants/{tenantId}/maintenance/{uuid}.jpg.
To back up locally:
# Install mc (MinIO client) then:
mc alias set local http://localhost:9000 fieldops fieldops123
mc mirror local/fieldops ./backup-photos
Running the E2E tests
# One-time browser install (downloads ~170 MB of Chromium)
pnpm --filter @repo/e2e install-browsers
# Run the happy-path test (starts both dev servers automatically)
pnpm test:e2e
The Playwright config force-sets AUTH_DEV_AUTOLOGIN=true for the child
servers, so the test does not depend on the developer's .env.
Expected: 1 passed in ~30 s.
Known limitations — v0.1 (demo only)
| Limitation | Detail | Target |
|---|---|---|
| No real authentication | AUTH_DEV_AUTOLOGIN=true lets anyone in as admin. The Credentials provider accepts any seeded email without a password. |
v0.2 pre-pilot |
| Operator picker, not TAG/card | Operator identity is chosen from a list rather than read from an RFID badge. | MY QUALITY module |
| No multi-tenant onboarding UI | Tenants are created via pnpm db:seed / SQL only. |
when 2nd customer onboards |
| No SLAs, alerts, or timers | DomainEvent rows are written and ready; reporting is not built yet. |
v0.2 |
| Single photo per request | No video, audio, or multiple photos. | when pilot asks |
| Safari / iOS Background Sync | Background Sync API is not supported on Safari; sync falls back to main-thread polling every 10 s when the tab is open. | acceptable for pilot |
| No push notifications | Polling at 5 s on the admin-web tab is the notification mechanism. | if pilot requires it |
| Dev-only storage | MinIO runs in Docker. No backup cron, no lifecycle policy, no cloud migration yet. | before pilot |
| No i18n | Hardcoded Portuguese (Mangualde plant). | v0.2 with pilot |
| No observability | Structured logs via Pino; no trace/metric pipeline. | when pilot requires it |
⚠️ NEVER deploy with
AUTH_DEV_AUTOLOGIN=true. That flag is a back door. The chokepoint isapps/operator-pwa/lib/auth.ts → resolveUser()(and the equivalent inapps/admin-web/lib/auth.ts). Replace with real authentication before any non-dev deployment.
Common commands
| Command | What it does |
|---|---|
pnpm install |
Install all workspace deps |
docker compose up -d |
Start Postgres + MinIO (detached) |
docker compose down |
Stop services (data persists in volumes) |
docker compose down -v |
Stop and delete all data |
pnpm --filter @repo/operator-pwa dev |
Operator PWA only (port 3000) |
pnpm --filter @repo/admin-web dev |
Admin web only (port 3001) |
pnpm db:migrate |
Apply pending Prisma migrations |
pnpm db:seed |
Re-seed demo data (idempotent, safe to re-run) |
pnpm db:reset |
Drop, recreate, migrate, seed |
pnpm db:studio |
Open Prisma Studio at http://localhost:5555 |
pnpm typecheck |
Typecheck every package |
pnpm test:e2e |
Run the happy-path Playwright test |
pnpm format |
Prettier write across the workspace |
pnpm tsx scripts/storage-smoke.ts |
Verify MinIO presigned upload/download |
pnpm tsx scripts/maintenance-smoke.ts |
Verify the full create→claim→resolve cycle |
Architecture notes
Multi-tenancy
Every Prisma model except Tenant carries a tenantId column. Tenant
scoping is enforced at the Prisma layer via an extension in
packages/db/src/tenant-extension.ts. Application code always goes
through ctx.db.* (scoped) — the unscoped ctx.prisma is a code-review
red flag.
Note on $transaction: The interactive-callback form of $transaction
receives a Prisma client that does NOT support $extends. Tenant scoping
inside transactions is done by manually injecting tenantId into each
where/data clause. See packages/api/src/routers/maintenance-request.ts
for the pattern.
Offline sync
The operator PWA stores pending requests in IndexedDB (Dexie 4). The sync
loop (lib/queue/sync.ts) runs in the main thread — triggered by the
online event, visibilitychange, and a 10 s polling interval. The
Background Sync API is registered opportunistically (works in Chrome,
ignored elsewhere). Communication between the sync loop and UI is via
BroadcastChannel('mai-call-sync').
Storage
Photos are uploaded directly from the browser to MinIO via S3 presigned
PUT URLs. The tRPC layer signs the URL; the browser performs the PUT. The
object key is returned and stored in the MaintenanceRequest row.
packages/storage implements the ObjectStorage interface with MinIO (via
AWS SDK v3 S3 protocol) and is portable to AWS S3, Cloudflare R2, or Wasabi
by changing the endpoint env var.
Troubleshooting
UNAUTHORIZED on every page — AUTH_DEV_AUTOLOGIN is false in your
.env. Set it to true for local dev, or sign in via the operator picker.
Tenant not found — the seed was wiped. Run pnpm db:seed.
DATABASE_URL not found — .env is missing or Docker Postgres is not
running. Run docker compose up -d then retry.
ERR_PNPM_IGNORED_BUILDS after adding a package — pnpm 11 blocks
postinstall scripts by default. Add the package to pnpm-workspace.yaml
allowBuilds:.
Playwright: port already in use — kill leftover dev servers:
# Windows
Get-Process node | Stop-Process -Force
# macOS / Linux
pkill -f 'next dev'
MinIO photos not loading in admin-web — verify MinIO is running
(docker compose ps) and that the fieldops bucket exists
(docker logs fieldops-minio-init-1).
License
Internal. All rights reserved.