rmancinasandClaude Opus 5 89611da202
Build and Push Images / Build jorgecuadros-web (push) Successful in 1m47s
Build and Push Images / Build jorgecuadros-api (push) Successful in 2m3s
feat(notificaciones): global send flags + editable schedules
The "Flags del envío" panel lived inside the Servicios tab and only
governed the four bulk jobs. The pólizas half had no debug at all, so
there was no way to test a renewal notice without mailing a real
customer. The panel now lives in the /notificaciones shell above the
tabs and both halves read it.

`debug` on the renewal path diverts to the same override inbox as the
servicios jobs and deliberately does NOT write the `RenewalNotice` row
or advance the sweep's `lastSuccessfulAt` — the customer was not
notified, so nothing may gate the letter they are still owed.
`ignoreDayRestriction` and `useEmailLimit` stay estado-de-cuenta-only
and are labelled as such.

Both automatic sweeps are now operator-editable. The renewal cadence
was a `@Cron("0 6 * * *")` literal and servicios had no automatic run
at all; both now resolve through `NotificationScheduleService`, which
stores the cadence in `app_settings` and reinstalls the cron job on
save — no redeploy, no restart. Defaults preserve current behaviour:
pólizas 06:00 daily, servicios off. A scheduled run never inherits the
UI flags; it always sends for real.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 12:23:19 -07:00
2026-08-02 02:06:12 +00:00

Jorge Cuadros & Asociados — Platform

Internal platform for a Baja California insurance brokerage and property-services firm: a single expedient joining each client's properties/services, insurance policies, account statement, and the firm's checkbook. It replaces a legacy PHP/Access app (see RESUME.md and PLAN.md for the full history and rebuild rationale).

The UI is Spanish-first; the codebase and this document are in English.


Stack

Layer Tech Port
Web Next.js 14 (App Router, React 18) 3000
API NestJS 10 · Passport local + express-session · Argon2 3001
Database MySQL 8 via Prisma 5 (@jorgecuadros/database workspace pkg) 3306
Migration Python 3 pipeline (legacy Access → staging → transforms)

Monorepo managed with pnpm workspaces. Node >= 20.


Repository layout

apps/
  web/        Next.js frontend  (@jorgecuadros/web)
  api/        NestJS backend    (@jorgecuadros/api)
    scripts/seed-user.mjs   idempotent admin seeder
packages/
  database/   Prisma schema + generated client (@jorgecuadros/database)
    prisma/schema.prisma
migration/    One-off Python ETL from the legacy Access DB (run_all.py)
docker/       Dockerfiles for api + web
docker-compose.yml          mysql + api + web
.env.example                copy to .env

API feature modules: auth, users, customers, policies, properties, billing, bank. Web routes: /clientes, /polizas, /servicios, /estado-cuenta, /banco (chequera), /catalogos, /usuarios, /login.


Prerequisites

  • Node.js >= 20 and pnpm (npm i -g pnpm)
  • Docker (for MySQL, or bring your own MySQL 8)
  • Python 3 — only if you run the legacy data migration

Run it locally (development)

1. Install

pnpm install

pnpm blocks postinstall build scripts by default; the trusted ones (argon2, prisma, @prisma/client, @prisma/engines, @nestjs/core) are allowlisted in pnpm-workspace.yaml, so the native builds run automatically.

2. Configure environment

cp .env.example .env

Then edit .env. For the Docker MySQL below the defaults already line up; just set a real SESSION_SECRET:

DATABASE_URL=mysql://jorgecuadros:jorgecuadros@localhost:3306/jorgecuadros
SESSION_SECRET=<any long random string>
WEB_ORIGIN=http://localhost:3000
NEXT_PUBLIC_API_ORIGIN=http://localhost:3001

The API loads DATABASE_URL, SESSION_SECRET, WEB_ORIGIN, and optional PORT (default 3001). The web app only needs NEXT_PUBLIC_API_ORIGIN.

3. Start MySQL

docker compose up -d mysql

(Or point DATABASE_URL at an existing MySQL 8 instance.)

4. Create the schema + generate the Prisma client

The schema is managed with prisma db push (no migration history committed):

pnpm --filter @jorgecuadros/database exec prisma db push
pnpm --filter @jorgecuadros/database generate

5. Seed a sign-in user

node apps/api/scripts/seed-user.mjs

Idempotent (upsert by email). Defaults — override with SEED_EMAIL, SEED_PASSWORD, SEED_NAME:

  • email: admin@jorgecuadros.local
  • password: ChangeMe!2026
  • role: ADMIN

6. Run the apps (two terminals)

# API  → http://localhost:3001
pnpm --filter @jorgecuadros/api start:dev

# Web  → http://localhost:3000
pnpm --filter @jorgecuadros/web dev

Root shortcuts also exist: pnpm dev:api, pnpm dev:web.

7. Log in

Open http://localhost:3000, sign in with the seeded credentials. Sessions are cookie-based and last 8 hours.


Run it with Docker (full stack)

Builds MySQL + API + web from docker-compose.yml:

export SESSION_SECRET=$(openssl rand -hex 32)
docker compose up --build

Web on http://localhost:3000, API on http://localhost:3001. SESSION_SECRET is required (compose fails without it). After first boot, push the schema and seed a user against the container DB:

docker compose exec api node apps/api/scripts/seed-user.mjs

Common commands

Task Command
Install pnpm install
Dev — API pnpm dev:api
Dev — Web pnpm dev:web
Build all pnpm build
Generate Prisma client pnpm prisma:generate
Push schema (dev) pnpm --filter @jorgecuadros/database exec prisma db push
Prisma Studio pnpm prisma:studio
API tests pnpm --filter @jorgecuadros/api test
Lint (web / api) pnpm --filter @jorgecuadros/web lint · ... /api lint
Seed admin user node apps/api/scripts/seed-user.mjs

Auth & roles

Session auth via Passport local strategy; passwords hashed with Argon2 (no plaintext, unlike the legacy app). Roles gate the UI and API — e.g. managing /catalogos and /usuarios requires the appropriate ability (ADMIN / MANAGER). New users are created by an admin in /usuarios; the first admin comes from the seed script above.


Legacy data migration (optional)

migration/ holds the one-off Python ETL that lifts data out of the old Microsoft Access database into MySQL.

pip install -r migration/requirements.txt
python migration/run_all.py

⚠️ run_all.py truncates and reloads all downstream tables. Never run an individual transform_*.py in isolation — it orphans dependent tables. See migration/RECONCILIATION.md for details.


Production notes

  • Use pnpm --filter @jorgecuadros/database exec prisma migrate deploy if/when a committed migration history is adopted; today dev uses db push.
  • Set a strong SESSION_SECRET and a locked-down DATABASE_URL.
  • The API expects WEB_ORIGIN to match the browser origin for session cookies.
  • Documents are stored in MinIO in the deployed environment (see RESUME.md).
S
Description
No description provided
Readme
2.2 MiB
Languages
TypeScript 82.3%
Python 10.1%
CSS 3.7%
JavaScript 3%
Shell 0.5%
Other 0.4%