# Context ## 1. Project Overview This project (often referred to as "Nexstar Graphs" or simply "Graphs") is a real-time sales and inventory dashboard. Its primary purpose is to ingest live webhook payloads from an external ERP (Tiny ERP) via n8n, securely store that data, and provide a visually rich, responsive dashboard for business analytics. It tracks total sales, product performance, customer behavior, and live inventory levels, while also providing tools for WhatsApp marketing campaigns. ## 2. Tech Stack & Tooling **Frontend:** * **Library:** React 19.2.5 * **Language:** TypeScript 6.0.2 * **Build Tool:** Vite 8.0.10 * **Styling:** Tailwind CSS 4.2.4 * **Icons:** Lucide React 1.14.0 * **Charts:** Recharts 3.8.1 * **Routing:** React Router DOM 7.14.2 **Backend:** * **Environment:** Node.js * **Framework:** Express 5.2.1 * **Database:** PostgreSQL (via `pg` 8.20.0) * **Authentication:** JWT (`jsonwebtoken` 9.0.3) * **CORS & Middleware:** `cors`, `body-parser` **Infrastructure & CI/CD:** * **Containerization:** Docker & Docker Compose * **Proxy:** Nginx * **CI/CD:** Gitea Actions (deploy.yml) * **Automation:** n8n (External trigger source) ## 3. Architecture & Design Decisions * **Decoupled Client-Server:** The frontend is a statically built SPA served by Nginx, communicating with an isolated Node.js API. * **Idempotent Database Operations:** Due to potential retry/spam from n8n webhooks, database insertions strictly use `INSERT ... ON CONFLICT DO UPDATE SET` (UPSERTs). The backend dynamically generates fallback IDs using composite keys (`Name_Date_Value`) to prevent historical data squashing when explicit `ID_Pedido` fields are missing. * **"Waiting Room" Debounce Pattern:** To prevent webhook spam during massive inventory updates, the `/api/stock` route intercepts payloads with large deltas (`>= 100`). It parks them in an in-memory dictionary grouped by Base Product Name, accumulates the numbers over a 30-minute setTimeout, and then executes a single aggregated webhook to N8N targeting the Top 100 buyers. * **Smart Polling vs. SSE:** Real-time UI updates are handled via client-side polling (`setInterval`) rather than Server-Sent Events (SSE) to bypass persistent connection drops caused by production reverse proxies. * **Client-Side Analytics:** The backend returns raw data arrays; sorting, mapping, deduplication (e.g., grouping unique orders by time), and chart metric generation are processed dynamically in the React `useMemo` hooks to reduce server load. ## 4. Directory Structure ```text / ├── .gitea/workflows/ # CI/CD pipeline definitions ├── backend/ # Node.js Express API │ ├── Dockerfile # Backend container definition │ ├── index.js # Core API logic, DB initialization, and Webhook handlers │ └── package.json ├── public/ # Static assets (Favicons) ├── src/ # React Frontend Application │ ├── components/ # Reusable UI elements (Layout, DateRangePicker) │ ├── pages/ # Route-level views (Dashboard, Products, Clients) │ ├── dataService.ts # Centralized API fetch logic and JWT handling │ ├── types.ts # Shared TypeScript interfaces │ └── main.tsx # React entry point ├── docker-compose.yml # Local orchestration and environment variable mapping ├── nginx.conf # Production web server routing └── vite.config.ts # Frontend build configuration ``` ## 5. Core Business Rules & Domain Entities * **Order Entity (`orders` table):** Tracks `cliente_nome`, `data_pedido`, `valor_pedido`, `produto_id`, `quantidade`, `valor_unitario`, `pedido_id`, and `cliente_fone`. * **Stock Entity (`stock` table):** Tracks `produto_id`, `nome`, `saldo` (absolute current inventory), and `delta_estoque`. The database treats the ERP's `saldo` as the Absolute Truth (overwriting existing values rather than performing math) to prevent desynchronization. * **WhatsApp Marketing Integration:** The system actively extracts phone numbers from incoming n8n payloads (checking `Fone_Cliente`, `fone`, or `celular`). Numbers are exposed in the UI for direct "Click-to-Chat" links and exported to CSV files for bulk marketing. * **Filter Persistence:** User preferences for Date Ranges, Sort options, and Auto-Refresh intervals are rigidly persisted to `localStorage` to survive page reloads. The "Hoje" (Today) date preset explicitly extends to `23:59:59.999` to ensure incoming real-time webhooks remain visible on the current day's graph. ## 6. CI/CD & Deployment * **Gitea Actions:** A workflow located in `.gitea/workflows/deploy.yml` triggers on pushes to the `main` branch. * **Docker Registry:** The pipeline builds the `frontend` and `backend` Docker images and pushes them directly to `gitea.blyzer.com.br/blyzer/`. * **Production Deployment:** Updates are deployed manually via Portainer by pulling the `latest` image tags from the Gitea registry and redeploying the stack. * **Environment Variables:** Security secrets (`API_KEY`, `JWT_SECRET`, `POSTGRES_PASSWORD`, `N8N_WHATSAPP_TRIGGER_URL`) are injected via the Portainer stack configuration and passed into containers via `docker-compose.yml`. ## 7. Environment Setup & Scripts **Running Locally:** 1. Start the database: ```bash docker compose up -d db ``` 2. Start the Backend (from `/backend`): ```bash npm install npm start ``` 3. Start the Frontend (from project root): ```bash npm install npm run dev # For HMR development npm run preview # For production build testing ``` **Building the Frontend:** ```bash npm run build ``` ## 8. Coding Standards & AI Directives * **Strict Type Safety:** Use explicit TypeScript interfaces (defined in `types.ts`). Avoid `any` where possible. Do not bypass type checks with `// @ts-ignore`. * **Idiomatic React:** Use functional components and hooks (`useState`, `useEffect`, `useMemo`). Complex data transformations (like merging arrays into chart-ready datasets) MUST be wrapped in `useMemo` to prevent unnecessary re-renders. * **Tailwind Architecture:** All styling must be handled via Tailwind CSS utility classes. Avoid custom CSS files unless defining global font families or root variables in `index.css`. * **Robust Data Handling:** Always implement graceful fallbacks for missing data. Never assume an API payload will contain all keys. (e.g., `item.id || item.ID_Pedido || ''`). * **Database Migrations:** There is no ORM (like Prisma or Sequelize). Table schemas and indexes are managed via raw SQL statements inside the `initDB()` function in `backend/index.js` using `IF NOT EXISTS` clauses for safe startup execution. * **API Security:** All backend modifications exposing or altering data MUST use the `verifyToken` middleware for frontend requests or `authenticateAPIKey` for external n8n webhooks.