Pedro Gomes 03c15fd069 MAI CALL - step 10
Passo 10 completo. AC verificado end-to-end:

Sem foto — row criada com photoKey=null ✓
Com foto — upload para MinIO via presigned PUT + row criada com photoKey correto + conteúdo verificado via presigned GET ✓
O que foi implementado:

/maintenance/new — Client Component com: select de posto (carregado via trpc.workstation.list), input de foto com compressão canvas (max 1600px, JPEG q=0.8), preview + botão remover, textarea com contador, submit que faz upload + create + redirect
/maintenance/sent — Server Component que mostra o clientRequestId e o botão "Voltar ao início"
Build de produção limpo com 7 rotas
2026-05-16 16:26:23 +01:00
2026-05-16 16:22:55 +01:00
2026-05-16 16:26:23 +01:00
2026-05-16 15:21:27 +01:00
2026-05-16 12:02:15 +01:00
2026-05-16 15:21:27 +01:00
2026-05-16 16:19:15 +01:00
2026-05-16 16:26:23 +01:00
2026-05-16 16:19:15 +01:00
2026-05-16 12:01:26 +01:00
2026-05-16 12:02:15 +01:00
2026-05-16 12:02:15 +01:00
2026-05-16 12:02:15 +01:00
2026-05-16 12:02:15 +01:00
2026-05-16 15:32:56 +01:00
2026-05-16 15:32:56 +01:00
2026-05-16 16:19:15 +01:00
2026-05-16 12:02:15 +01:00
2026-05-16 12:01:26 +01:00
2026-05-16 12:02:15 +01:00
2026-05-16 16:19:15 +01:00

FieldOps

Modular industrial SaaS monorepo. This is the scaffold phase — no business features yet. The goal of this branch is to prove the end-to-end wiring: client → tRPC → Prisma → Postgres, with multi-tenancy enforced from day zero.

What's here

apps/
  operator-pwa/      Next 15 — operator console (port 3000)
  admin-web/         Next 15 — backoffice placeholder (port 3001)
packages/
  db/                Prisma 6 schema + tenant-scoping extension
  api/               tRPC v11 routers
  ui/                Shared shadcn-style components
  domain/            Pure domain logic (empty in this phase)
  config/            tsconfig / eslint / tailwind presets
e2e/                 Playwright smoke test

Prerequisites

  • Node.js 22+ (use nvm use or install matching .nvmrc)
  • pnpm 11+ (corepack enable pnpm or npm i -g pnpm)
  • Docker Desktop with the engine running (provides Postgres)
  • Git

From-zero setup

These steps assume a fresh clone with no node_modules, no Docker volumes, no .env. Run them in order from the repo root.

# 1. environment
cp .env.example .env
#    Open .env and set AUTH_DEV_AUTOLOGIN="true" for local development.
#    (Default is "false" on purpose — see "Auth" below.)

# 2. install
pnpm install

# 3. start Postgres
docker compose up -d

# 4. apply the schema
pnpm db:migrate

# 5. seed the demo tenant
pnpm db:seed

# 6. start the operator app
pnpm --filter @repo/operator-pwa dev

Open http://localhost:3000 — you should see a Connected card with Tenant: Demo Factory.

Run the E2E

In a separate shell (with the dev server NOT running, or with reuseExistingServer left on — Playwright handles either):

pnpm --filter @repo/e2e install-browsers   # one-time, downloads Chromium
pnpm test:e2e

The Playwright config force-sets AUTH_DEV_AUTOLOGIN=true for the dev server it launches, so the test does not depend on the developer's .env.

Auth

This scaffold has no real authentication. Auth.js v5 is wired up with a single Credentials provider that accepts any email present in the seeded User table — no password check.

The AUTH_DEV_AUTOLOGIN env flag controls a server-side fallback:

  • AUTH_DEV_AUTOLOGIN=false (default in .env.example) — requests with no Auth.js session are unauthenticated. protectedProcedure returns 401.
  • AUTH_DEV_AUTOLOGIN=true — requests with no session silently resolve to the seeded admin@demo.local user, skipping the login UI.

⚠️ Never set AUTH_DEV_AUTOLOGIN=true in production. The fallback is a back door intended only for local dev and CI. The chokepoint is apps/operator-pwa/lib/auth.ts → resolveUser(). Replace it with real authentication before any non-dev deployment.

Multi-tenancy

Every Prisma model except Tenant has a tenantId column. Tenant scoping is enforced at runtime by an extension in packages/db/src/tenant-extension.ts — read the header comment in that file for the full list of operations it covers and the operations it does not ($queryRaw, interactive $transaction with external clients, etc.).

Call sites get the scoped client from the tRPC context: ctx.db.*. The unscoped root client (ctx.prisma) is available for the rare cross-tenant case but should be a red flag at PR review.

tRPC

The operator app uses both tRPC paths:

  • RSC callerapps/operator-pwa/lib/trpc/server.ts — direct in-process invocation from Server Components. Default for reads.
  • Client hooksapps/operator-pwa/lib/trpc/client.ts@trpc/react-query against /api/trpc. Default for mutations and client-rendered reads.

The home page exercises both: the Connected card is rendered server-side; the Client-side ping card uses useQuery. Both must succeed for the hybrid wiring to be considered green.

Common commands

Command What it does
pnpm install Install all workspace deps
docker compose up -d Start Postgres
docker compose down Stop Postgres (data persists)
docker compose down -v Stop Postgres and drop the volume
pnpm dev Run all dev tasks via Turbo
pnpm --filter @repo/operator-pwa dev Operator app only
pnpm --filter @repo/admin-web dev Admin app only (port 3001)
pnpm db:migrate Apply pending Prisma migrations
pnpm db:reset Drop, recreate, migrate, seed
pnpm db:seed Re-seed the demo tenant (idempotent)
pnpm db:studio Open Prisma Studio
pnpm typecheck Typecheck every package
pnpm test:e2e Run Playwright
pnpm format Prettier write

Versions of note

Package Version Why
Node 22 LTS Required by Next 15
pnpm 11 Workspace + allowBuilds security feature
Next.js 15 App Router, React 19
React 19
Prisma 6 Kept on 6.x — Prisma 7 mandates the new prisma-client ESM generator, which is a separate migration
Auth.js (next-auth) 5.0 beta v5 split-config pattern (edge-safe middleware + Node providers)
tRPC 11 Stable on the v11 series
Tailwind 3 Conservative choice; v4 swap is a separate spike
shadcn/ui components inlined Not the CLI scaffold — components live in packages/ui/src/components

Troubleshooting

ERR_PNPM_IGNORED_BUILDS after adding a dep — pnpm 11 blocks postinstall scripts unless approved. Edit pnpm-workspace.yaml allowBuilds: and set the offending package to true (or false if you don't want its postinstall to run).

Page loads but says UNAUTHORIZED — your .env has AUTH_DEV_AUTOLOGIN=false (the default). Flip it to true for local dev, or sign in through Auth.js.

Page errors with Tenant not found — the seeded tenant was wiped. Run pnpm db:seed again.

Migration fails with Environment variable not found: DATABASE_URL — make sure .env exists at the repo root and Docker Postgres is running.

Playwright complains the port is busy — kill any leftover dev servers: on Windows Get-Process node | Stop-Process -Force; macOS/Linux pkill -f 'next dev'.

License

Internal. All rights reserved.

Description
No description provided
Readme 1.7 MiB
Languages
TypeScript 68.6%
JavaScript 30.2%
CSS 1.2%