# 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 ```text n8n -> POST /api/data -> PostgreSQL orders -> dashboard ``` The endpoint accepts a single order item or an array. Requests must include: ```text x-api-key: Content-Type: application/json ``` ### Stock Ingestion ```text 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 ```text 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: ```json { "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: ```text OLIST_CLIENT_ID OLIST_CLIENT_SECRET OLIST_REDIRECT_URI=https:///api/olist/oauth/callback OLIST_FRONTEND_URL=https:// 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: ```bash 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: ```bash docker compose up -d db ``` Start the backend: ```bash cd backend npm install npm start ``` Start the frontend: ```bash npm install npm run dev ``` Default local URLs: ```text 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: ```text 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 ```bash npm run lint npm run build ``` For backend syntax checks: ```bash cd backend node --check index.js node --check services/campaignService.js node --check services/stockService.js ```