@polymorphism-tech/morph-spec 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +279 -0
- package/bin/morph-spec.js +53 -0
- package/content/.claude/commands/morph-apply.md +66 -0
- package/content/.claude/commands/morph-archive.md +79 -0
- package/content/.claude/commands/morph-costs.md +206 -0
- package/content/.claude/commands/morph-infra.md +209 -0
- package/content/.claude/commands/morph-proposal.md +60 -0
- package/content/.claude/commands/morph-status.md +71 -0
- package/content/.claude/settings.local.json +15 -0
- package/content/.claude/skills/infra/bicep-architect.md +419 -0
- package/content/.claude/skills/infra/container-specialist.md +437 -0
- package/content/.claude/skills/infra/devops-engineer.md +405 -0
- package/content/.claude/skills/integrations/asaas-financial.md +333 -0
- package/content/.claude/skills/integrations/azure-identity.md +309 -0
- package/content/.claude/skills/integrations/clerk-auth.md +290 -0
- package/content/.claude/skills/specialists/azure-architect.md +142 -0
- package/content/.claude/skills/specialists/cost-guardian.md +110 -0
- package/content/.claude/skills/specialists/ef-modeler.md +200 -0
- package/content/.claude/skills/specialists/hangfire-orchestrator.md +245 -0
- package/content/.claude/skills/specialists/ms-agent-expert.md +209 -0
- package/content/.claude/skills/specialists/po-pm-advisor.md +197 -0
- package/content/.claude/skills/specialists/standards-architect.md +78 -0
- package/content/.claude/skills/specialists/ui-ux-designer.md +325 -0
- package/content/.claude/skills/stacks/dotnet-blazor.md +352 -0
- package/content/.claude/skills/stacks/dotnet-nextjs.md +402 -0
- package/content/.claude/skills/stacks/shopify.md +445 -0
- package/content/.morph/archive/.gitkeep +25 -0
- package/content/.morph/config/agents.json +149 -0
- package/content/.morph/config/config.template.json +96 -0
- package/content/.morph/examples/api-nextjs/README.md +241 -0
- package/content/.morph/examples/api-nextjs/contracts.ts +307 -0
- package/content/.morph/examples/api-nextjs/spec.md +399 -0
- package/content/.morph/examples/api-nextjs/tasks.md +168 -0
- package/content/.morph/examples/micro-saas/README.md +125 -0
- package/content/.morph/examples/micro-saas/contracts.cs +358 -0
- package/content/.morph/examples/micro-saas/decisions.md +246 -0
- package/content/.morph/examples/micro-saas/spec.md +236 -0
- package/content/.morph/examples/micro-saas/tasks.md +150 -0
- package/content/.morph/examples/multi-agent/README.md +309 -0
- package/content/.morph/examples/multi-agent/contracts.cs +433 -0
- package/content/.morph/examples/multi-agent/spec.md +479 -0
- package/content/.morph/examples/multi-agent/tasks.md +185 -0
- package/content/.morph/features/.gitkeep +25 -0
- package/content/.morph/project.md +159 -0
- package/content/.morph/specs/.gitkeep +20 -0
- package/content/.morph/standards/architecture.md +190 -0
- package/content/.morph/standards/azure.md +184 -0
- package/content/.morph/standards/coding.md +342 -0
- package/content/.morph/templates/agent.cs +172 -0
- package/content/.morph/templates/component.razor +239 -0
- package/content/.morph/templates/contracts.cs +217 -0
- package/content/.morph/templates/decisions.md +106 -0
- package/content/.morph/templates/infra/app-insights.bicep +63 -0
- package/content/.morph/templates/infra/container-app-env.bicep +49 -0
- package/content/.morph/templates/infra/container-app.bicep +156 -0
- package/content/.morph/templates/infra/key-vault.bicep +91 -0
- package/content/.morph/templates/infra/main.bicep +155 -0
- package/content/.morph/templates/infra/parameters.dev.json +23 -0
- package/content/.morph/templates/infra/parameters.prod.json +23 -0
- package/content/.morph/templates/infra/sql-database.bicep +103 -0
- package/content/.morph/templates/infra/storage.bicep +106 -0
- package/content/.morph/templates/integrations/asaas-client.cs +387 -0
- package/content/.morph/templates/integrations/asaas-webhook.cs +351 -0
- package/content/.morph/templates/integrations/azure-identity-config.cs +288 -0
- package/content/.morph/templates/integrations/clerk-config.cs +258 -0
- package/content/.morph/templates/job.cs +171 -0
- package/content/.morph/templates/migration.cs +83 -0
- package/content/.morph/templates/proposal.md +155 -0
- package/content/.morph/templates/recap.md +105 -0
- package/content/.morph/templates/repository.cs +141 -0
- package/content/.morph/templates/saas/subscription.cs +347 -0
- package/content/.morph/templates/saas/tenant.cs +338 -0
- package/content/.morph/templates/service.cs +139 -0
- package/content/.morph/templates/spec.md +147 -0
- package/content/.morph/templates/tasks.md +235 -0
- package/content/.morph/templates/test.cs +239 -0
- package/content/CLAUDE.md +318 -0
- package/package.json +50 -0
- package/src/commands/doctor.js +132 -0
- package/src/commands/init.js +121 -0
- package/src/commands/update.js +84 -0
- package/src/utils/file-copier.js +50 -0
- package/src/utils/logger.js +32 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# API .NET + Next.js Example
|
|
2
|
+
|
|
3
|
+
Exemplo de arquitetura full-stack com .NET API backend e Next.js frontend.
|
|
4
|
+
|
|
5
|
+
## Stack
|
|
6
|
+
|
|
7
|
+
| Camada | Tecnologia |
|
|
8
|
+
|--------|------------|
|
|
9
|
+
| **Frontend** | Next.js 14 (App Router) |
|
|
10
|
+
| **Backend** | .NET 8 Minimal APIs |
|
|
11
|
+
| **Database** | SQL Server / PostgreSQL |
|
|
12
|
+
| **Auth** | Clerk (SSO) |
|
|
13
|
+
| **API Communication** | REST + React Query |
|
|
14
|
+
| **Infra** | Azure Container Apps |
|
|
15
|
+
|
|
16
|
+
## Arquitetura
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
20
|
+
│ Next.js Frontend │
|
|
21
|
+
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
|
|
22
|
+
│ │ Pages │ │ Components │ │ Hooks │ │
|
|
23
|
+
│ │ /dashboard │ │ DataTable │ │ useProducts() │ │
|
|
24
|
+
│ │ /products │ │ Forms │ │ useOrders() │ │
|
|
25
|
+
│ │ /orders │ │ Charts │ │ useAuth() │ │
|
|
26
|
+
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
|
|
27
|
+
└─────────────────────────────────────────────────────────────┘
|
|
28
|
+
│
|
|
29
|
+
│ REST API
|
|
30
|
+
▼
|
|
31
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
32
|
+
│ .NET API │
|
|
33
|
+
│ ┌─────────────────────────────────────────────────────┐ │
|
|
34
|
+
│ │ Endpoints: /api/products, /api/orders, /api/users │ │
|
|
35
|
+
│ └─────────────────────────────────────────────────────┘ │
|
|
36
|
+
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
|
|
37
|
+
│ │ Services │ │ Validators │ │ Middlewares │ │
|
|
38
|
+
│ └──────────────┘ └──────────────┘ └──────────────────┘ │
|
|
39
|
+
└─────────────────────────────────────────────────────────────┘
|
|
40
|
+
│
|
|
41
|
+
▼
|
|
42
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
43
|
+
│ Infrastructure │
|
|
44
|
+
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
|
|
45
|
+
│ │ EF Core │ │ Redis │ │ Blob Storage │ │
|
|
46
|
+
│ │ Repository │ │ Cache │ │ (files) │ │
|
|
47
|
+
│ └──────────────┘ └──────────────┘ └──────────────────┘ │
|
|
48
|
+
└─────────────────────────────────────────────────────────────┘
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Estrutura de Pastas
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
project/
|
|
55
|
+
├── src/
|
|
56
|
+
│ ├── api/ # .NET Backend
|
|
57
|
+
│ │ ├── Api/
|
|
58
|
+
│ │ │ ├── Endpoints/
|
|
59
|
+
│ │ │ │ ├── ProductEndpoints.cs
|
|
60
|
+
│ │ │ │ ├── OrderEndpoints.cs
|
|
61
|
+
│ │ │ │ └── UserEndpoints.cs
|
|
62
|
+
│ │ │ ├── Middleware/
|
|
63
|
+
│ │ │ └── Program.cs
|
|
64
|
+
│ │ ├── Application/
|
|
65
|
+
│ │ │ ├── Services/
|
|
66
|
+
│ │ │ └── Validators/
|
|
67
|
+
│ │ ├── Domain/
|
|
68
|
+
│ │ │ ├── Entities/
|
|
69
|
+
│ │ │ └── Interfaces/
|
|
70
|
+
│ │ └── Infrastructure/
|
|
71
|
+
│ │ ├── Data/
|
|
72
|
+
│ │ └── Repositories/
|
|
73
|
+
│ │
|
|
74
|
+
│ └── web/ # Next.js Frontend
|
|
75
|
+
│ ├── app/
|
|
76
|
+
│ │ ├── (auth)/
|
|
77
|
+
│ │ │ ├── sign-in/
|
|
78
|
+
│ │ │ └── sign-up/
|
|
79
|
+
│ │ ├── (dashboard)/
|
|
80
|
+
│ │ │ ├── dashboard/
|
|
81
|
+
│ │ │ ├── products/
|
|
82
|
+
│ │ │ └── orders/
|
|
83
|
+
│ │ ├── layout.tsx
|
|
84
|
+
│ │ └── page.tsx
|
|
85
|
+
│ ├── components/
|
|
86
|
+
│ │ ├── ui/
|
|
87
|
+
│ │ └── shared/
|
|
88
|
+
│ ├── hooks/
|
|
89
|
+
│ │ ├── use-products.ts
|
|
90
|
+
│ │ └── use-orders.ts
|
|
91
|
+
│ ├── lib/
|
|
92
|
+
│ │ ├── api-client.ts
|
|
93
|
+
│ │ └── utils.ts
|
|
94
|
+
│ └── types/
|
|
95
|
+
│
|
|
96
|
+
├── infra/ # Infrastructure as Code
|
|
97
|
+
│ ├── main.bicep
|
|
98
|
+
│ └── modules/
|
|
99
|
+
│
|
|
100
|
+
└── docker-compose.yml
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Quick Start
|
|
104
|
+
|
|
105
|
+
### Backend (.NET)
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
cd src/api
|
|
109
|
+
dotnet restore
|
|
110
|
+
dotnet ef database update
|
|
111
|
+
dotnet run
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Frontend (Next.js)
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
cd src/web
|
|
118
|
+
npm install
|
|
119
|
+
npm run dev
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Docker Compose
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
docker-compose up -d
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Features
|
|
129
|
+
|
|
130
|
+
### Backend
|
|
131
|
+
- Minimal APIs com OpenAPI
|
|
132
|
+
- FluentValidation para validação
|
|
133
|
+
- EF Core com Repository Pattern
|
|
134
|
+
- JWT Bearer Authentication
|
|
135
|
+
- Health Checks
|
|
136
|
+
- Structured Logging
|
|
137
|
+
|
|
138
|
+
### Frontend
|
|
139
|
+
- App Router (Next.js 14)
|
|
140
|
+
- Server Components + Client Components
|
|
141
|
+
- React Query para data fetching
|
|
142
|
+
- Tailwind CSS + shadcn/ui
|
|
143
|
+
- Form handling com React Hook Form
|
|
144
|
+
- TypeScript estrito
|
|
145
|
+
|
|
146
|
+
## Autenticação
|
|
147
|
+
|
|
148
|
+
### Fluxo
|
|
149
|
+
|
|
150
|
+
```mermaid
|
|
151
|
+
sequenceDiagram
|
|
152
|
+
participant User
|
|
153
|
+
participant NextJS
|
|
154
|
+
participant Clerk
|
|
155
|
+
participant API
|
|
156
|
+
|
|
157
|
+
User->>NextJS: Acessa /sign-in
|
|
158
|
+
NextJS->>Clerk: Redirect para Clerk
|
|
159
|
+
User->>Clerk: Login
|
|
160
|
+
Clerk-->>NextJS: JWT Token
|
|
161
|
+
NextJS->>API: Request com Bearer Token
|
|
162
|
+
API->>Clerk: Valida token (JWKS)
|
|
163
|
+
API-->>NextJS: Response
|
|
164
|
+
NextJS-->>User: Renderiza página
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Configuração Clerk (Next.js)
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
// middleware.ts
|
|
171
|
+
import { clerkMiddleware } from "@clerk/nextjs/server";
|
|
172
|
+
|
|
173
|
+
export default clerkMiddleware();
|
|
174
|
+
|
|
175
|
+
export const config = {
|
|
176
|
+
matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)"],
|
|
177
|
+
};
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Configuração JWT (API)
|
|
181
|
+
|
|
182
|
+
```csharp
|
|
183
|
+
// Program.cs
|
|
184
|
+
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
|
185
|
+
.AddJwtBearer(options =>
|
|
186
|
+
{
|
|
187
|
+
options.Authority = builder.Configuration["Clerk:Authority"];
|
|
188
|
+
options.TokenValidationParameters = new TokenValidationParameters
|
|
189
|
+
{
|
|
190
|
+
ValidateAudience = false,
|
|
191
|
+
NameClaimType = "sub"
|
|
192
|
+
};
|
|
193
|
+
});
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## API Client (Frontend)
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
// lib/api-client.ts
|
|
200
|
+
import { auth } from "@clerk/nextjs/server";
|
|
201
|
+
|
|
202
|
+
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL;
|
|
203
|
+
|
|
204
|
+
export async function apiClient<T>(
|
|
205
|
+
endpoint: string,
|
|
206
|
+
options: RequestInit = {}
|
|
207
|
+
): Promise<T> {
|
|
208
|
+
const { getToken } = auth();
|
|
209
|
+
const token = await getToken();
|
|
210
|
+
|
|
211
|
+
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
|
|
212
|
+
...options,
|
|
213
|
+
headers: {
|
|
214
|
+
"Content-Type": "application/json",
|
|
215
|
+
Authorization: `Bearer ${token}`,
|
|
216
|
+
...options.headers,
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
if (!response.ok) {
|
|
221
|
+
throw new Error(`API Error: ${response.statusText}`);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return response.json();
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Custos Estimados
|
|
229
|
+
|
|
230
|
+
| Recurso | Tier | Custo/mês |
|
|
231
|
+
|---------|------|-----------|
|
|
232
|
+
| Container App (API) | Consumption | ~$0-5 |
|
|
233
|
+
| Container App (Web) | Consumption | ~$0-5 |
|
|
234
|
+
| SQL Database | Free 32GB | $0 |
|
|
235
|
+
| Clerk | Free 10k MAU | $0 |
|
|
236
|
+
|
|
237
|
+
**Total**: < $10/mês para MVPs
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
*MORPH-SPEC by Polymorphism Tech*
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
// ==============================================================================
|
|
2
|
+
// API .NET + Next.js - Frontend Contracts (TypeScript)
|
|
3
|
+
// Types compartilhados entre API e Frontend
|
|
4
|
+
// ==============================================================================
|
|
5
|
+
|
|
6
|
+
// ==============================================================================
|
|
7
|
+
// PAGINATION
|
|
8
|
+
// ==============================================================================
|
|
9
|
+
|
|
10
|
+
export interface PaginationParams {
|
|
11
|
+
page?: number;
|
|
12
|
+
pageSize?: number;
|
|
13
|
+
sortBy?: string;
|
|
14
|
+
sortDesc?: boolean;
|
|
15
|
+
search?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface PagedResult<T> {
|
|
19
|
+
items: T[];
|
|
20
|
+
totalItems: number;
|
|
21
|
+
page: number;
|
|
22
|
+
pageSize: number;
|
|
23
|
+
totalPages: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ==============================================================================
|
|
27
|
+
// ENTITIES
|
|
28
|
+
// ==============================================================================
|
|
29
|
+
|
|
30
|
+
export interface Product {
|
|
31
|
+
id: number;
|
|
32
|
+
name: string;
|
|
33
|
+
description?: string;
|
|
34
|
+
price: number;
|
|
35
|
+
stock: number;
|
|
36
|
+
category?: string;
|
|
37
|
+
imageUrl?: string;
|
|
38
|
+
isActive: boolean;
|
|
39
|
+
createdAt: string;
|
|
40
|
+
updatedAt?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface Order {
|
|
44
|
+
id: number;
|
|
45
|
+
orderNumber: string;
|
|
46
|
+
customerId: number;
|
|
47
|
+
customer?: Customer;
|
|
48
|
+
status: OrderStatus;
|
|
49
|
+
items: OrderItem[];
|
|
50
|
+
subtotal: number;
|
|
51
|
+
tax: number;
|
|
52
|
+
total: number;
|
|
53
|
+
notes?: string;
|
|
54
|
+
createdAt: string;
|
|
55
|
+
updatedAt?: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface OrderItem {
|
|
59
|
+
id: number;
|
|
60
|
+
productId: number;
|
|
61
|
+
product?: Product;
|
|
62
|
+
quantity: number;
|
|
63
|
+
unitPrice: number;
|
|
64
|
+
total: number;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface Customer {
|
|
68
|
+
id: number;
|
|
69
|
+
name: string;
|
|
70
|
+
email: string;
|
|
71
|
+
phone?: string;
|
|
72
|
+
address?: Address;
|
|
73
|
+
createdAt: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface Address {
|
|
77
|
+
street: string;
|
|
78
|
+
number: string;
|
|
79
|
+
complement?: string;
|
|
80
|
+
neighborhood: string;
|
|
81
|
+
city: string;
|
|
82
|
+
state: string;
|
|
83
|
+
zipCode: string;
|
|
84
|
+
country: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface User {
|
|
88
|
+
id: string;
|
|
89
|
+
email: string;
|
|
90
|
+
name?: string;
|
|
91
|
+
imageUrl?: string;
|
|
92
|
+
role: UserRole;
|
|
93
|
+
createdAt: string;
|
|
94
|
+
lastLoginAt?: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ==============================================================================
|
|
98
|
+
// ENUMS
|
|
99
|
+
// ==============================================================================
|
|
100
|
+
|
|
101
|
+
export type OrderStatus =
|
|
102
|
+
| "Pending"
|
|
103
|
+
| "Confirmed"
|
|
104
|
+
| "Processing"
|
|
105
|
+
| "Shipped"
|
|
106
|
+
| "Delivered"
|
|
107
|
+
| "Cancelled";
|
|
108
|
+
|
|
109
|
+
export type UserRole = "Admin" | "Manager" | "User" | "Viewer";
|
|
110
|
+
|
|
111
|
+
export type ProductCategory =
|
|
112
|
+
| "Electronics"
|
|
113
|
+
| "Clothing"
|
|
114
|
+
| "Food"
|
|
115
|
+
| "Books"
|
|
116
|
+
| "Other";
|
|
117
|
+
|
|
118
|
+
// ==============================================================================
|
|
119
|
+
// REQUEST DTOs
|
|
120
|
+
// ==============================================================================
|
|
121
|
+
|
|
122
|
+
export interface CreateProductRequest {
|
|
123
|
+
name: string;
|
|
124
|
+
description?: string;
|
|
125
|
+
price: number;
|
|
126
|
+
stock: number;
|
|
127
|
+
category?: string;
|
|
128
|
+
imageUrl?: string;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export interface UpdateProductRequest {
|
|
132
|
+
name?: string;
|
|
133
|
+
description?: string;
|
|
134
|
+
price?: number;
|
|
135
|
+
stock?: number;
|
|
136
|
+
category?: string;
|
|
137
|
+
imageUrl?: string;
|
|
138
|
+
isActive?: boolean;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface CreateOrderRequest {
|
|
142
|
+
customerId: number;
|
|
143
|
+
items: CreateOrderItemRequest[];
|
|
144
|
+
notes?: string;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export interface CreateOrderItemRequest {
|
|
148
|
+
productId: number;
|
|
149
|
+
quantity: number;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export interface UpdateOrderStatusRequest {
|
|
153
|
+
status: OrderStatus;
|
|
154
|
+
notes?: string;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export interface CreateCustomerRequest {
|
|
158
|
+
name: string;
|
|
159
|
+
email: string;
|
|
160
|
+
phone?: string;
|
|
161
|
+
address?: Address;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ==============================================================================
|
|
165
|
+
// RESPONSE DTOs
|
|
166
|
+
// ==============================================================================
|
|
167
|
+
|
|
168
|
+
export interface ApiResponse<T> {
|
|
169
|
+
data: T;
|
|
170
|
+
success: boolean;
|
|
171
|
+
message?: string;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export interface ApiError {
|
|
175
|
+
type: string;
|
|
176
|
+
title: string;
|
|
177
|
+
status: number;
|
|
178
|
+
detail?: string;
|
|
179
|
+
instance?: string;
|
|
180
|
+
errors?: Record<string, string[]>;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export interface DashboardStats {
|
|
184
|
+
totalProducts: number;
|
|
185
|
+
totalOrders: number;
|
|
186
|
+
totalCustomers: number;
|
|
187
|
+
totalRevenue: number;
|
|
188
|
+
ordersToday: number;
|
|
189
|
+
revenueToday: number;
|
|
190
|
+
recentOrders: Order[];
|
|
191
|
+
topProducts: ProductSales[];
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export interface ProductSales {
|
|
195
|
+
product: Product;
|
|
196
|
+
totalSold: number;
|
|
197
|
+
totalRevenue: number;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ==============================================================================
|
|
201
|
+
// FILTER DTOs
|
|
202
|
+
// ==============================================================================
|
|
203
|
+
|
|
204
|
+
export interface ProductFilters extends PaginationParams {
|
|
205
|
+
category?: string;
|
|
206
|
+
minPrice?: number;
|
|
207
|
+
maxPrice?: number;
|
|
208
|
+
inStock?: boolean;
|
|
209
|
+
isActive?: boolean;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export interface OrderFilters extends PaginationParams {
|
|
213
|
+
status?: OrderStatus;
|
|
214
|
+
customerId?: number;
|
|
215
|
+
startDate?: string;
|
|
216
|
+
endDate?: string;
|
|
217
|
+
minTotal?: number;
|
|
218
|
+
maxTotal?: number;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export interface CustomerFilters extends PaginationParams {
|
|
222
|
+
city?: string;
|
|
223
|
+
state?: string;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ==============================================================================
|
|
227
|
+
// FORM SCHEMAS (Zod)
|
|
228
|
+
// ==============================================================================
|
|
229
|
+
|
|
230
|
+
// Exemplo de uso com Zod:
|
|
231
|
+
/*
|
|
232
|
+
import { z } from "zod";
|
|
233
|
+
|
|
234
|
+
export const createProductSchema = z.object({
|
|
235
|
+
name: z.string().min(1, "Name is required").max(100),
|
|
236
|
+
description: z.string().max(500).optional(),
|
|
237
|
+
price: z.number().positive("Price must be positive"),
|
|
238
|
+
stock: z.number().int().nonnegative("Stock cannot be negative"),
|
|
239
|
+
category: z.string().optional(),
|
|
240
|
+
imageUrl: z.string().url().optional(),
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
export type CreateProductFormData = z.infer<typeof createProductSchema>;
|
|
244
|
+
|
|
245
|
+
export const createOrderSchema = z.object({
|
|
246
|
+
customerId: z.number().int().positive(),
|
|
247
|
+
items: z.array(z.object({
|
|
248
|
+
productId: z.number().int().positive(),
|
|
249
|
+
quantity: z.number().int().positive("Quantity must be at least 1"),
|
|
250
|
+
})).min(1, "At least one item is required"),
|
|
251
|
+
notes: z.string().max(500).optional(),
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
export type CreateOrderFormData = z.infer<typeof createOrderSchema>;
|
|
255
|
+
*/
|
|
256
|
+
|
|
257
|
+
// ==============================================================================
|
|
258
|
+
// REACT QUERY KEYS
|
|
259
|
+
// ==============================================================================
|
|
260
|
+
|
|
261
|
+
export const queryKeys = {
|
|
262
|
+
products: {
|
|
263
|
+
all: ["products"] as const,
|
|
264
|
+
list: (filters: ProductFilters) => ["products", "list", filters] as const,
|
|
265
|
+
detail: (id: number) => ["products", "detail", id] as const,
|
|
266
|
+
},
|
|
267
|
+
orders: {
|
|
268
|
+
all: ["orders"] as const,
|
|
269
|
+
list: (filters: OrderFilters) => ["orders", "list", filters] as const,
|
|
270
|
+
detail: (id: number) => ["orders", "detail", id] as const,
|
|
271
|
+
},
|
|
272
|
+
customers: {
|
|
273
|
+
all: ["customers"] as const,
|
|
274
|
+
list: (filters: CustomerFilters) =>
|
|
275
|
+
["customers", "list", filters] as const,
|
|
276
|
+
detail: (id: number) => ["customers", "detail", id] as const,
|
|
277
|
+
},
|
|
278
|
+
dashboard: ["dashboard"] as const,
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
// ==============================================================================
|
|
282
|
+
// API ENDPOINTS
|
|
283
|
+
// ==============================================================================
|
|
284
|
+
|
|
285
|
+
export const apiEndpoints = {
|
|
286
|
+
products: {
|
|
287
|
+
list: "/api/products",
|
|
288
|
+
detail: (id: number) => `/api/products/${id}`,
|
|
289
|
+
create: "/api/products",
|
|
290
|
+
update: (id: number) => `/api/products/${id}`,
|
|
291
|
+
delete: (id: number) => `/api/products/${id}`,
|
|
292
|
+
},
|
|
293
|
+
orders: {
|
|
294
|
+
list: "/api/orders",
|
|
295
|
+
detail: (id: number) => `/api/orders/${id}`,
|
|
296
|
+
create: "/api/orders",
|
|
297
|
+
updateStatus: (id: number) => `/api/orders/${id}/status`,
|
|
298
|
+
cancel: (id: number) => `/api/orders/${id}/cancel`,
|
|
299
|
+
},
|
|
300
|
+
customers: {
|
|
301
|
+
list: "/api/customers",
|
|
302
|
+
detail: (id: number) => `/api/customers/${id}`,
|
|
303
|
+
create: "/api/customers",
|
|
304
|
+
update: (id: number) => `/api/customers/${id}`,
|
|
305
|
+
},
|
|
306
|
+
dashboard: "/api/dashboard",
|
|
307
|
+
};
|