docs: add project context and remove boilerplate
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 44s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 44s
This commit is contained in:
@@ -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
99
CONTEXT.md
Normal 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
169
README.md
@@ -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
|
||||
```
|
||||
|
||||
@@ -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);
|
||||
42
fake-data.js
42
fake-data.js
@@ -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();
|
||||
184
src/App.css
184
src/App.css
@@ -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 |
@@ -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 |
146
src/data.json
146
src/data.json
@@ -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
|
||||
}
|
||||
]
|
||||
@@ -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();
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user