Cauê Faleiros 9960fb7986
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 46s
Return production plans to supplies hub
2026-08-27 19:43:09 -03:00
2026-06-16 15:46:04 -03:00
2026-07-28 13:03:51 -03:00
2026-06-26 11:11:36 -03:00
2026-07-09 13:23:25 -03:00

Nexstar Graphs

Real-time sales and stock dashboard for Nexstar. The app receives Tiny ERP data through n8n webhooks, stores it in PostgreSQL, and renders sales, products, clients, stock, and WhatsApp campaign data in a React dashboard.

Stack

  • Frontend: React, TypeScript, Vite, Tailwind CSS, Recharts
  • Backend: Node.js, Express, PostgreSQL, JWT, API-key webhook auth
  • Runtime: Docker Compose, Nginx, n8n

Main Flows

Sales Ingestion

n8n -> POST /api/data -> PostgreSQL orders -> dashboard

The endpoint accepts a single order item or an array. Requests must include:

x-api-key: <API_KEY>
Content-Type: application/json

Stock Ingestion

n8n -> POST /api/stock -> PostgreSQL stock + campaign queue

Positive stock deltas are queued for WhatsApp campaigns. The scheduled processor groups pending queue rows by base product name and sends a campaign only when the accumulated pending delta reaches at least 100.

Scheduled WhatsApp Campaigns

n8n schedule at 12:00/18:00 BRT
-> POST /api/internal/process-stock-campaigns
-> backend calls N8N_WHATSAPP_TRIGGER_URL
-> n8n WhatsApp workflow sends templates

The scheduled endpoint is API-key protected and returns a summary:

{
  "claimed": 0,
  "sentGroups": 0,
  "skippedGroups": 0,
  "failedGroups": 0,
  "pendingBelowThresholdGroups": 0
}

Native Olist V3 composition sync

Graphs can connect directly to Olist V3 from Cadastros, import manufactured-product structures, and create/update the local catalog and consumption references. Configure these backend environment variables in Portainer before connecting:

OLIST_CLIENT_ID
OLIST_CLIENT_SECRET
OLIST_REDIRECT_URI=https://<backend-host>/api/olist/oauth/callback
OLIST_FRONTEND_URL=https://<frontend-host>
OLIST_TOKEN_ENCRYPTION_KEY=<32-byte base64 key or 64-character hex key>
OLIST_SYNC_ENABLED=true

OLIST_TOKEN_ENCRYPTION_KEY must be stable between deploys because Graphs uses it to encrypt OAuth tokens stored in PostgreSQL. The initial sync is full; later scheduled runs are incremental and use the product update date. Manual JSON import remains available as a fallback.

Graphs waits at least 2.5 seconds between every Olist API request (24 requests/minute). This keeps capacity available for other Tiny/Olist integrations. The first catalog scan caches product IDs, SKUs, descriptions, and units; each checked BOM is persisted immediately, so a restarted full sync resumes from the remaining products. Later runs use Olist's dataAlteracao filter instead of listing the full catalog. OLIST_SYNC_REQUEST_DELAY_MS can increase that delay but cannot lower it below 2500.

Cutting Workbook Import

Manual cutting/replenishment workbooks can be normalized into JSON before they are persisted or shown in the app:

python3 backend/scripts/normalize_cut_workbook.py "/path/to/NECESSIDADE DE CORTE.xlsx" --pretty -o /tmp/necessidade_corte_normalized.json

The output includes purchase need rows, production orders, finished stock, stock plus OP availability, real purchase need, outside items, and cut-plan sections by family (BLCS, BLMC, BLOS, BLPM). The importer reads cached workbook values and reports broken formula references instead of making the app depend on Excel formulas at runtime.

Local Development

Start PostgreSQL:

docker compose up -d db

Start the backend:

cd backend
npm install
npm start

Start the frontend:

npm install
npm run dev

Default local URLs:

Frontend: http://127.0.0.1:3002
Backend:  http://127.0.0.1:3004

Vite may choose a different frontend port if 3002 is already in use.

Environment

Copy .env.example and configure production secrets in the runtime environment:

POSTGRES_USER
POSTGRES_PASSWORD
POSTGRES_DB
API_KEY
N8N_WHATSAPP_TRIGGER_URL
ADMIN_EMAIL
ADMIN_PASSWORD
JWT_SECRET
TURNSTILE_SITE_KEY
TURNSTILE_SECRET

TURNSTILE_SECRET enables backend CAPTCHA enforcement on /api/login. Set TURNSTILE_SITE_KEY with Cloudflare's public site key so the login page can load the verification widget at runtime.

The backend also accepts VITE_TURNSTILE_SITE_KEY as a site-key alias and TURNSTILE_SECRET_KEY as a secret alias for older deployments.

Validation

npm run lint
npm run build

For backend syntax checks:

cd backend
node --check index.js
node --check services/campaignService.js
node --check services/stockService.js
Description
No description provided
Readme 7.1 MiB
Languages
TypeScript 65.8%
JavaScript 32.6%
Python 1%
CSS 0.5%