docs: add project context and remove boilerplate
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 44s

This commit is contained in:
Cauê Faleiros
2026-05-27 16:18:10 -03:00
parent 5e0bb1d83a
commit 62a0bcfbc9
12 changed files with 206 additions and 517 deletions

View File

@@ -13,6 +13,7 @@ POSTGRES_DB=graphdb
# --- Application Security ---
# The API key used by n8n to authenticate with the backend
API_KEY=nexstar_secret_key_123
N8N_WHATSAPP_TRIGGER_URL=https://n8n.example.com/webhook/whatsapp-stock
# --- Dashboard Login Credentials ---
ADMIN_EMAIL=admin@admin.com

99
CONTEXT.md Normal file
View File

@@ -0,0 +1,99 @@
# 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.

169
README.md
View File

@@ -1,73 +1,116 @@
# React + TypeScript + Vite
# Nexstar Graphs
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
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.
Currently, two official plugins are available:
## Stack
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs)
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)
- Frontend: React, TypeScript, Vite, Tailwind CSS, Recharts
- Backend: Node.js, Express, PostgreSQL, JWT, API-key webhook auth
- Runtime: Docker Compose, Nginx, n8n
## React Compiler
## Main Flows
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
### Sales Ingestion
## Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
```js
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Remove tseslint.configs.recommended and replace with this
tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
tseslint.configs.stylisticTypeChecked,
// Other configs...
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```text
n8n -> POST /api/data -> PostgreSQL orders -> dashboard
```
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
The endpoint accepts a single order item or an array. Requests must include:
```js
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Enable lint rules for React
reactX.configs['recommended-typescript'],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```text
x-api-key: <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
}
```
## 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:3001
Backend: http://127.0.0.1:3004
```
Vite may choose a different frontend port if `3001` 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
```
## 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
```

View File

@@ -1,33 +0,0 @@
const products = [];
// Group 1: High Quantity, Low Price (Top 10 in Bar Chart, won't show in Pie Chart)
for (let i = 0; i < 10; i++) {
products.push({
Nome_Cliente: "Fake Client A" + i,
Data_Pedido: "06-05-2026",
Valor_Pedido: 100,
ID_Produto: "QA" + i,
Descricao_Produto: "Produto Muito Vendido " + i,
Quantidade: 1000 + i, // High sales
Valor_Unitario: 0.10 // Low revenue
});
}
// Group 2: Low Quantity, High Price (Top 10 in Pie Chart, won't show in Bar Chart)
for (let i = 0; i < 10; i++) {
products.push({
Nome_Cliente: "Fake Client B" + i,
Data_Pedido: "06-05-2026",
Valor_Pedido: 10000,
ID_Produto: "QB" + i,
Descricao_Produto: "Produto Muito Caro " + i,
Quantidade: 1, // Low sales
Valor_Unitario: 10000 + i // High revenue
});
}
fetch('http://localhost:3004/api/data', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'x-api-key': 'nexstar_secret_key_123' },
body: JSON.stringify(products)
}).then(res => console.log("Status:", res.status)).catch(console.error);

View File

@@ -1,42 +0,0 @@
// Clear previous data for a clean slate
const { Pool } = require('pg');
const pool = new Pool({ connectionString: 'postgres://graphuser:graphpassword@localhost:5432/graphdb' });
async function run() {
await pool.query('TRUNCATE TABLE orders RESTART IDENTITY;');
// Group 1: High Quantity, Low Price (Top 10 in Bar Chart, won't show in Pie Chart)
const group1 = Array.from({length: 10}, (_, i) => ({
Nome_Cliente: "Fake Client A" + i,
Data_Pedido: "06-05-2026",
Valor_Pedido: 100,
ID_Produto: "QA" + i,
Descricao_Produto: "Produto Muito Vendido " + i,
Quantidade: 1000 + i, // High sales
Valor_Unitario: 0.10 // Low revenue
}));
// Group 2: Low Quantity, High Price (Top 10 in Pie Chart, won't show in Bar Chart)
const group2 = Array.from({length: 10}, (_, i) => ({
Nome_Cliente: "Fake Client B" + i,
Data_Pedido: "06-05-2026",
Valor_Pedido: 10000,
ID_Produto: "QB" + i,
Descricao_Produto: "Produto Muito Caro " + i,
Quantidade: 1, // Low sales
Valor_Unitario: 10000 + i // High revenue
}));
const products = [...group1, ...group2];
const res = await fetch('http://localhost:3004/api/data', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'x-api-key': 'nexstar_secret_key_123' },
body: JSON.stringify(products)
});
console.log(res.status);
process.exit(0);
}
run();

View File

@@ -1,184 +0,0 @@
.counter {
font-size: 16px;
padding: 5px 10px;
border-radius: 5px;
color: var(--accent);
background: var(--accent-bg);
border: 2px solid transparent;
transition: border-color 0.3s;
margin-bottom: 24px;
&:hover {
border-color: var(--accent-border);
}
&:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
}
.hero {
position: relative;
.base,
.framework,
.vite {
inset-inline: 0;
margin: 0 auto;
}
.base {
width: 170px;
position: relative;
z-index: 0;
}
.framework,
.vite {
position: absolute;
}
.framework {
z-index: 1;
top: 34px;
height: 28px;
transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg)
scale(1.4);
}
.vite {
z-index: 0;
top: 107px;
height: 26px;
width: auto;
transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg)
scale(0.8);
}
}
#center {
display: flex;
flex-direction: column;
gap: 25px;
place-content: center;
place-items: center;
flex-grow: 1;
@media (max-width: 1024px) {
padding: 32px 20px 24px;
gap: 18px;
}
}
#next-steps {
display: flex;
border-top: 1px solid var(--border);
text-align: left;
& > div {
flex: 1 1 0;
padding: 32px;
@media (max-width: 1024px) {
padding: 24px 20px;
}
}
.icon {
margin-bottom: 16px;
width: 22px;
height: 22px;
}
@media (max-width: 1024px) {
flex-direction: column;
text-align: center;
}
}
#docs {
border-right: 1px solid var(--border);
@media (max-width: 1024px) {
border-right: none;
border-bottom: 1px solid var(--border);
}
}
#next-steps ul {
list-style: none;
padding: 0;
display: flex;
gap: 8px;
margin: 32px 0 0;
.logo {
height: 18px;
}
a {
color: var(--text-h);
font-size: 16px;
border-radius: 6px;
background: var(--social-bg);
display: flex;
padding: 6px 12px;
align-items: center;
gap: 8px;
text-decoration: none;
transition: box-shadow 0.3s;
&:hover {
box-shadow: var(--shadow);
}
.button-icon {
height: 18px;
width: 18px;
}
}
@media (max-width: 1024px) {
margin-top: 20px;
flex-wrap: wrap;
justify-content: center;
li {
flex: 1 1 calc(50% - 8px);
}
a {
width: 100%;
justify-content: center;
box-sizing: border-box;
}
}
}
#spacer {
height: 88px;
border-top: 1px solid var(--border);
@media (max-width: 1024px) {
height: 48px;
}
}
.ticks {
position: relative;
width: 100%;
&::before,
&::after {
content: '';
position: absolute;
top: -4.5px;
border: 5px solid transparent;
}
&::before {
left: 0;
border-left-color: var(--border);
}
&::after {
right: 0;
border-right-color: var(--border);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

Before

Width:  |  Height:  |  Size: 4.0 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.5 KiB

View File

@@ -1,146 +0,0 @@
[
{
"Nome_Cliente": "Luiz Felipe Oliveira Silva",
"Data_Pedido": "30-04-2026",
"Valor_Pedido": 19.9,
"ID_Produto": "951438842",
"Descricao_Produto": "IMPRESSÃO DTF PERSONALIZADO 57X100 (1 METRO)",
"Quantidade": 1,
"Valor_Unitario": 19.9
},
{
"Nome_Cliente": "61.855.899 WALTER PONCE JUNIOR",
"Data_Pedido": "30-04-2026",
"Valor_Pedido": 69.65,
"ID_Produto": "951438842",
"Descricao_Produto": "IMPRESSÃO DTF PERSONALIZADO 57X100 (1 METRO)",
"Quantidade": 3,
"Valor_Unitario": 19.9
},
{
"Nome_Cliente": "61.855.899 WALTER PONCE JUNIOR",
"Data_Pedido": "30-04-2026",
"Valor_Pedido": 69.65,
"ID_Produto": "978770094",
"Descricao_Produto": "IMPRESSÃO DTF PERSONALIZADO 57X0,50 (50 CENTÍMETROS)",
"Quantidade": 1,
"Valor_Unitario": 9.95
},
{
"Nome_Cliente": "Guilherme de Souza",
"Data_Pedido": "30-04-2026",
"Valor_Pedido": 65.66,
"ID_Produto": "951438842",
"Descricao_Produto": "IMPRESSÃO DTF PERSONALIZADO 57X100 (1 METRO)",
"Quantidade": 3,
"Valor_Unitario": 20.51889
},
{
"Nome_Cliente": "Guilherme de Souza",
"Data_Pedido": "30-04-2026",
"Valor_Pedido": 65.66,
"ID_Produto": "978776637",
"Descricao_Produto": "IMPRESSÃO DTF PERSONALIZADO 57X0,20 (20 CENTÍMETROS)",
"Quantidade": 1,
"Valor_Unitario": 4.103778
},
{
"Nome_Cliente": "Guilherme de Souza",
"Data_Pedido": "30-04-2026",
"Valor_Pedido": 24.54,
"ID_Produto": "919483299",
"Descricao_Produto": "BASE LISA CAMISETA COR PRETO TAMANHO - P",
"Quantidade": 1,
"Valor_Unitario": 12.27009
},
{
"Nome_Cliente": "Guilherme de Souza",
"Data_Pedido": "30-04-2026",
"Valor_Pedido": 24.54,
"ID_Produto": "919483307",
"Descricao_Produto": "BASE LISA CAMISETA COR PRETO TAMANHO - G",
"Quantidade": 1,
"Valor_Unitario": 12.27009
},
{
"Nome_Cliente": "61.855.899 WALTER PONCE JUNIOR",
"Data_Pedido": "30-04-2026",
"Valor_Pedido": 95.2,
"ID_Produto": "919483303",
"Descricao_Produto": "BASE LISA CAMISETA COR PRETO TAMANHO - M",
"Quantidade": 2,
"Valor_Unitario": 11.9
},
{
"Nome_Cliente": "61.855.899 WALTER PONCE JUNIOR",
"Data_Pedido": "30-04-2026",
"Valor_Pedido": 95.2,
"ID_Produto": "919483311",
"Descricao_Produto": "BASE LISA CAMISETA COR PRETO TAMANHO - GG",
"Quantidade": 2,
"Valor_Unitario": 11.9
},
{
"Nome_Cliente": "61.855.899 WALTER PONCE JUNIOR",
"Data_Pedido": "30-04-2026",
"Valor_Pedido": 95.2,
"ID_Produto": "976044109",
"Descricao_Produto": "BASE LISA CAMISETA COR MARINHO TAMANHO - GG",
"Quantidade": 2,
"Valor_Unitario": 11.9
},
{
"Nome_Cliente": "61.855.899 WALTER PONCE JUNIOR",
"Data_Pedido": "30-04-2026",
"Valor_Pedido": 95.2,
"ID_Produto": "919483255",
"Descricao_Produto": "BASE LISA CAMISETA COR PEROLA TAMANHO - G",
"Quantidade": 1,
"Valor_Unitario": 11.9
},
{
"Nome_Cliente": "61.855.899 WALTER PONCE JUNIOR",
"Data_Pedido": "30-04-2026",
"Valor_Pedido": 95.2,
"ID_Produto": "919483299",
"Descricao_Produto": "BASE LISA CAMISETA COR PRETO TAMANHO - P",
"Quantidade": 1,
"Valor_Unitario": 11.9
},
{
"Nome_Cliente": "Francieli Campos",
"Data_Pedido": "30-04-2026",
"Valor_Pedido": 224.95,
"ID_Produto": "978761156",
"Descricao_Produto": "IMPRESSÃO DTF PERSONALIZADO 57X100 (10 METROS)",
"Quantidade": 1,
"Valor_Unitario": 199
},
{
"Nome_Cliente": "Daniela Gutierrez",
"Data_Pedido": "30-04-2026",
"Valor_Pedido": 217.45,
"ID_Produto": "951438842",
"Descricao_Produto": "IMPRESSÃO DTF PERSONALIZADO 57X100 (1 METRO)",
"Quantidade": 10,
"Valor_Unitario": 19.9
},
{
"Nome_Cliente": "Joyce Pretel",
"Data_Pedido": "30-04-2026",
"Valor_Pedido": 22.47,
"ID_Produto": "951540701",
"Descricao_Produto": "BONÉ - PRETO",
"Quantidade": 3,
"Valor_Unitario": 7.49
},
{
"Nome_Cliente": "61.855.899 WALTER PONCE JUNIOR",
"Data_Pedido": "30-04-2026",
"Valor_Pedido": 59.7,
"ID_Produto": "951438842",
"Descricao_Produto": "IMPRESSÃO DTF PERSONALIZADO 57X100 (1 METRO)",
"Quantidade": 3,
"Valor_Unitario": 19.9
}
]

View File

@@ -1,15 +0,0 @@
async function run() {
const loginRes = await fetch('http://localhost:3004/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: 'admin@admin.com', password: 'admin123' })
});
const { token } = await loginRes.json();
const dataRes = await fetch('http://localhost:3004/api/data?start=2020-01-01&end=2030-01-01', {
headers: { 'Authorization': `Bearer ${token}` }
});
const data = await dataRes.json();
const fabricio = data.find(d => d.Nome_Cliente.includes('Teste Hoje'));
console.log(fabricio);
}
run();

View File

@@ -1,32 +0,0 @@
[
{
"Nome_Cliente": "Fabrcio Araujo leme",
"Data_Pedido": "13/02/2025",
"Valor_Pedido": 94.95,
"ID_Pedido": "942384335",
"ID_Produto": "942384336",
"Descricao_Produto": "Camiseta Plus Size Premium Algodão Estampada Paris Tira G4 Grafite",
"Quantidade": "1.0000",
"Valor_Unitario": "31.650000"
},
{
"Nome_Cliente": "Fabrcio Araujo leme",
"Data_Pedido": "13/02/2025",
"Valor_Pedido": 94.95,
"ID_Pedido": "942384335",
"ID_Produto": "942384339",
"Descricao_Produto": "Camiseta Plus Size Premium Algodão Estampada Fichas Rolando G4 Preto",
"Quantidade": "1.0000",
"Valor_Unitario": "31.650000"
},
{
"Nome_Cliente": "Fabrcio Araujo leme",
"Data_Pedido": "13/02/2025",
"Valor_Pedido": 94.95,
"ID_Pedido": "942384335",
"ID_Produto": "942384342",
"Descricao_Produto": "Camiseta Plus Size Unissex T-Shirt Premium Sorte Nas Cartas G4 Preto",
"Quantidade": "1.0000",
"Valor_Unitario": "31.650000"
}
]