feat(ADM-018): completed feature
This commit is contained in:
342
project/specs-admin/SPEC.md
Normal file
342
project/specs-admin/SPEC.md
Normal file
@@ -0,0 +1,342 @@
|
||||
# Admin Panel SDD — MercadoDeVida
|
||||
|
||||
## 1. Repository Assessment
|
||||
|
||||
### 1.1 Estructura actual
|
||||
|
||||
```
|
||||
mercadodevida/
|
||||
├── project/ # Backend (Fastify + Node.js)
|
||||
│ └── src/modules/ # 20 módulos de dominio
|
||||
├── frontend/ # Storefront (Next.js 16 + Tailwind)
|
||||
│ └── src/
|
||||
│ ├── app/ # Rutas Next.js
|
||||
│ ├── components/ # Componentes React
|
||||
│ ├── contexts/ # CartContext, AuthContext
|
||||
│ ├── lib/ # api.ts (fetch functions)
|
||||
│ └── types/ # API types
|
||||
├── specs/ # specs de backend (F-001–F-035)
|
||||
└── work/ # artefactos por feature
|
||||
```
|
||||
|
||||
### 1.2 Ubicación recomendada para Admin
|
||||
|
||||
```
|
||||
apps/admin/ # Nueva app Next.js independiente
|
||||
├── app/
|
||||
│ ├── (auth)/
|
||||
│ │ └── login/
|
||||
│ ├── (dashboard)/
|
||||
│ │ ├── layout.tsx # Shell con sidebar
|
||||
│ │ ├── page.tsx # Dashboard
|
||||
│ │ ├── products/
|
||||
│ │ ├── orders/
|
||||
│ │ ├── customers/
|
||||
│ │ ├── categories/
|
||||
│ │ ├── brands/
|
||||
│ │ ├── promotions/
|
||||
│ │ ├── inventory/
|
||||
│ │ ├── reviews/
|
||||
│ │ ├── cms/
|
||||
│ │ └── settings/
|
||||
│ └── api/ # Proxies al backend (auth)
|
||||
├── features/ # Feature-sliced modules
|
||||
│ ├── auth/
|
||||
│ ├── products/
|
||||
│ ├── orders/
|
||||
│ └── ...
|
||||
├── components/ # Componentes compartidos
|
||||
│ ├── ui/ # Primitivos de diseño
|
||||
│ └── admin/ # Componentes domain-specific
|
||||
├── lib/
|
||||
│ ├── api-client.ts # Cliente HTTP único
|
||||
│ └── permissions.ts # can("action")
|
||||
├── types/ # Tipos compartidos
|
||||
└── middleware/ # Auth middleware
|
||||
```
|
||||
|
||||
### 1.3 Por qué NO en `frontend/src/`
|
||||
|
||||
- Admin y Storefront tienen lifecycle diferentes
|
||||
- Admin es Desktop-first, Storefront es Mobile-first
|
||||
- Admin necesita auth compleja, Storefront no
|
||||
- Mezclaría contextos (CartContext no aplica a admin)
|
||||
- El Shell de Admin es completamente diferente
|
||||
|
||||
### 1.4 Build system
|
||||
|
||||
- Backend: `cd project && npm run build` → `dist/`
|
||||
- Frontend: `cd frontend && npm run build` → `.next/`
|
||||
- Admin: `cd apps/admin && npm run build` → `.next/`
|
||||
- Monorepo en nivel de scripts (`npm run` en root)
|
||||
|
||||
---
|
||||
|
||||
## 2. API Capability Assessment
|
||||
|
||||
### 2.1 Endpoints disponibles para Admin
|
||||
|
||||
| Feature | Método | Endpoint | Auth | Admin | Notas |
|
||||
|---------------|--------|----------------------------------|------|-------|--------------------------|
|
||||
| Products | GET | GET /products/search | No | — | público, paginado |
|
||||
| Products | POST | POST /products | ✓ | ✓ | Crear producto |
|
||||
| Products | PATCH | PATCH /products/:id | ✓ | ✓ | Editar producto |
|
||||
| Products | GET | GET /products/:id/variants | No | — | Variantes |
|
||||
| Products | GET | GET /products/:id/images | No | — | Imágenes |
|
||||
| Products | POST | POST /products/:id/images | ✓ | ✓ | Subir imagen |
|
||||
| Products | DELETE | DELETE /products/:id/images/:id | ✓ | ✓ | Eliminar imagen |
|
||||
| Products | POST | POST /products/:id/variants | ✓ | ✓ | Crear variante |
|
||||
| Products | PATCH | PATCH /products/:id/variants/:id | ✓ | ✓ | Editar variante |
|
||||
| Products | PATCH | PATCH /products/:id/rich-data | ✓ | ✓ | Datos enriquecidos |
|
||||
| Brands | GET | GET /brands | No | — | Listado público |
|
||||
| Brands | GET | GET /marca/:slug | No | — | Detalle público |
|
||||
| Brands | POST | POST /brands | ✓ | ✓ | Crear marca |
|
||||
| Brands | PATCH | PATCH /brands/:id | ✓ | ✓ | Editar marca |
|
||||
| Categories | GET | GET /categories/tree | No | — | Árbol categorías |
|
||||
| Categories | GET | GET /categoria/:slug | No | — | Detalle público |
|
||||
| Categories | POST | POST /categories | ✓ | ✓ | Crear categoría |
|
||||
| Categories | PATCH | PATCH /categories/:id | ✓ | ✓ | Editar categoría |
|
||||
| Categories | DELETE | DELETE /categories/:id | ✓ | ✓ | Eliminar categoría |
|
||||
| Orders | GET | GET /orders | ✓ | ✓ | **NUEVO — listado admin** |
|
||||
| Orders | GET | GET /orders/:id/admin | ✓ | ✓ | **NUEVO — detalle admin**|
|
||||
| Orders | POST | POST /orders/:id/transitions/admin | ✓ | ✓ | **NUEVO — transición** |
|
||||
| Orders | GET | GET /orders/:id | ✓ | — | Detalle cliente |
|
||||
| Orders | POST | POST /orders/:id/transitions | ✓ | — | Transición cliente |
|
||||
| Inventory | GET | GET /inventory/:variantId/availability | No | — | Disponibilidad |
|
||||
| Inventory | PUT | PUT /inventory/:variantId/stock | ✓ | ✓ | Ajustar stock |
|
||||
| Inventory | POST | POST /inventory/:variantId/reservations | No | — | Reservas |
|
||||
| Payments | GET | GET /payments/orders/:id/transactions | ✓ | — | Transacciones |
|
||||
| Users | GET | GET /users | ✓ | ✓ | Listado clientes |
|
||||
| Users | GET | GET /users/:id | ✓ | ✓ | Detalle cliente |
|
||||
| Users | PATCH | PATCH /users/:id | ✓ | ✓ | Editar cliente |
|
||||
| Users | GET | GET /users/:id/addresses | ✓ | ✓ | Direcciones |
|
||||
| Users | POST | POST /users/:id/addresses | ✓ | ✓ | Crear dirección |
|
||||
| Users | PATCH | PATCH /users/:id/addresses/:id | ✓ | ✓ | Editar dirección |
|
||||
| Users | DELETE | DELETE /users/:id/addresses/:id | ✓ | ✓ | Eliminar dirección |
|
||||
| Promotions | POST | POST /promotions | ✓ | ✓ | Crear promoción |
|
||||
| Reviews | GET | GET /reviews?productId= | No | — | Reseñas publicadas |
|
||||
| Reviews | PATCH | PATCH /reviews/:id/moderate | ✓ | ✓ | Moderar reseña |
|
||||
| CMS | GET | GET /cms/pages/:slug | No | — | Página pública |
|
||||
| CMS | POST | POST /cms/pages | ✓ | ✓ | Crear página |
|
||||
| CMS | PATCH | PATCH /cms/pages/:id | ✓ | ✓ | Editar página |
|
||||
| CMS | POST | POST /cms/pages/:id/publish | ✓ | ✓ | Publicar |
|
||||
| CMS | POST | POST /cms/pages/:id/unpublish | ✓ | ✓ | Despublicar |
|
||||
| Shipping | POST | POST /shipping/zones | ✓ | ✓ | Crear zona envío |
|
||||
| Shipping | POST | POST /shipping/methods | ✓ | ✓ | Crear método envío |
|
||||
|
||||
### 2.2 Endpoints FALTANTES en el backend
|
||||
|
||||
| # | Capacidad requerida | Endpoint necesario | Prioridad |
|
||||
|---|--------------------------------|---------------------------------------|-----------|
|
||||
| BD-01 | Auth/me (sesión actual) | GET /auth/me | CRÍTICA |
|
||||
| BD-02 | Listar promociones | GET /promotions | ALTA |
|
||||
| BD-03 | Editar promoción | PATCH /promotions/:id | ALTA |
|
||||
| BD-04 | Eliminar promoción | DELETE /promotions/:id | MEDIA |
|
||||
| BD-05 | Listar reseñas (todas) | GET /reviews/admin | ALTA |
|
||||
| BD-06 | Actualizar producto estado | PATCH /products/:id/state | MEDIA |
|
||||
| BD-07 | Eliminar producto | DELETE /products/:id | MEDIA |
|
||||
| BD-08 | Eliminar marca | DELETE /brands/:id | MEDIA |
|
||||
| BD-09 | Bulk stock adjustment | POST /inventory/bulk-adjust | BAJA |
|
||||
| BD-10 | Audit log explorer | GET /admin/audit | MEDIA |
|
||||
| BD-11 | Dashboard stats | GET /admin/stats | MEDIA |
|
||||
|
||||
### 2.3 Roles existentes en backend
|
||||
|
||||
```typescript
|
||||
type Role = 'customer' | 'admin';
|
||||
```
|
||||
|
||||
Solo existen dos roles. No hay granularidad (Catalog Manager, Order Manager, etc).
|
||||
|
||||
---
|
||||
|
||||
## 3. Admin API Client Design
|
||||
|
||||
### 3.1 Cliente HTTP único
|
||||
|
||||
```typescript
|
||||
// apps/admin/lib/api-client.ts
|
||||
class ApiClient {
|
||||
constructor(private baseUrl: string, private getToken: () => string | null) {}
|
||||
|
||||
async request<T>(method, path, body?, opts?): Promise<T>
|
||||
async get<T>(path): Promise<T>
|
||||
async post<T>(path, body): Promise<T>
|
||||
async patch<T>(path, body): Promise<T>
|
||||
async delete<T>(path): Promise<T>
|
||||
}
|
||||
|
||||
// Feature API adapters
|
||||
// apps/admin/features/orders/api.ts
|
||||
export const ordersApi = {
|
||||
list: (params) => client.get('/orders', params),
|
||||
get: (id) => client.get(`/orders/${id}/admin`),
|
||||
transition: (id, state) => client.post(`/orders/${id}/transitions/admin`, { state }),
|
||||
};
|
||||
```
|
||||
|
||||
### 3.2 Permisos
|
||||
|
||||
```typescript
|
||||
// apps/admin/lib/permissions.ts
|
||||
type Permission =
|
||||
| 'products.read' | 'products.write' | 'products.delete'
|
||||
| 'orders.read' | 'orders.write'
|
||||
| 'inventory.read' | 'inventory.write'
|
||||
| 'customers.read' | 'customers.write'
|
||||
| 'categories.read' | 'categories.write' | 'categories.delete'
|
||||
| 'brands.read' | 'brands.write' | 'brands.delete'
|
||||
| 'promotions.read' | 'promotions.write'
|
||||
| 'reviews.read' | 'reviews.moderate'
|
||||
| 'cms.read' | 'cms.write';
|
||||
|
||||
export function can(user: User, permission: Permission): boolean {
|
||||
if (user.role === 'admin') return true;
|
||||
// Granular permissions would go here when backend supports them
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Arquitectura de rutas Admin
|
||||
|
||||
```
|
||||
apps/admin/app/
|
||||
├── (auth)/
|
||||
│ └── login/
|
||||
│ └── page.tsx
|
||||
├── (dashboard)/
|
||||
│ ├── layout.tsx # AdminShell: sidebar + header
|
||||
│ ├── page.tsx # /admin — Dashboard
|
||||
│ ├── products/
|
||||
│ │ ├── page.tsx # /admin/products — Listado
|
||||
│ │ └── [id]/
|
||||
│ │ └── page.tsx # /admin/products/[id] — Editor
|
||||
│ ├── orders/
|
||||
│ │ ├── page.tsx # /admin/orders — Listado
|
||||
│ │ └── [id]/
|
||||
│ │ └── page.tsx # /admin/orders/[id] — Detalle
|
||||
│ ├── customers/
|
||||
│ │ ├── page.tsx # /admin/customers
|
||||
│ │ └── [id]/
|
||||
│ │ └── page.tsx
|
||||
│ ├── categories/
|
||||
│ │ └── page.tsx # /admin/categories
|
||||
│ ├── brands/
|
||||
│ │ └── page.tsx # /admin/brands
|
||||
│ ├── inventory/
|
||||
│ │ └── page.tsx # /admin/inventory
|
||||
│ ├── promotions/
|
||||
│ │ └── page.tsx # /admin/promotions
|
||||
│ ├── reviews/
|
||||
│ │ └── page.tsx # /admin/reviews
|
||||
│ ├── cms/
|
||||
│ │ └── page.tsx # /admin/cms
|
||||
│ └── settings/
|
||||
│ └── page.tsx # /admin/settings
|
||||
└── api/
|
||||
└── auth/
|
||||
└── [...nextauth]/route.ts # Auth handler
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. RBAC Matrix
|
||||
|
||||
| Feature | admin | customer |
|
||||
|-------------|-------|----------|
|
||||
| Products R | ✓ | — |
|
||||
| Products W | ✓ | — |
|
||||
| Orders R | ✓ | own only |
|
||||
| Orders W | ✓ | — |
|
||||
| Inventory R | ✓ | — |
|
||||
| Inventory W | ✓ | — |
|
||||
| Customers R | ✓ | — |
|
||||
| Customers W | ✓ | own only |
|
||||
| Categories | ✓ | — |
|
||||
| Brands | ✓ | — |
|
||||
| Promotions | ✓ | — |
|
||||
| Reviews | ✓ | own only |
|
||||
| CMS | ✓ | — |
|
||||
| Audit | ✓ | — |
|
||||
|
||||
---
|
||||
|
||||
## 6. MVP Scope
|
||||
|
||||
### Priority 1 (crítico para operar la tienda)
|
||||
|
||||
1. Auth — login, logout, sesión
|
||||
2. Admin Shell — sidebar, layout, permisos
|
||||
3. Products — listado, editor (nombre, precio, stock, imágenes, estado)
|
||||
4. Orders — listado, detalle, transiciones de estado
|
||||
5. Inventory — vista de stock, ajuste rápido
|
||||
|
||||
### Priority 2 (operación normal)
|
||||
|
||||
6. Customers — listado, detalle de cliente
|
||||
7. Categories — CRUD básico
|
||||
8. Brands — CRUD básico
|
||||
9. Dashboard — stats operativos
|
||||
|
||||
### Priority 3 (mejora)
|
||||
|
||||
10. Promotions — CRUD
|
||||
11. Reviews — moderación
|
||||
12. CMS — gestión de páginas
|
||||
13. Audit log
|
||||
|
||||
---
|
||||
|
||||
## 7. Backend Dependencies (BD-01 a BD-11)
|
||||
|
||||
Ver sección 2.2. Cada una es un ticket de backend separado.
|
||||
|
||||
---
|
||||
|
||||
## 8. Parallelization Plan
|
||||
|
||||
```
|
||||
FASE 1 — Foundation (sequencial)
|
||||
│
|
||||
├── ADM-001: Nuevo proyecto Next.js en apps/admin
|
||||
├── ADM-002: API Client + types
|
||||
├── ADM-003: Auth (BD-01 + login page)
|
||||
│
|
||||
FASE 2 — Admin Shell (sequencial)
|
||||
│
|
||||
├── ADM-004: Admin layout (sidebar, header, permisos nav)
|
||||
│
|
||||
FASE 3 — MVP Features (paralelo)
|
||||
│
|
||||
├── Stream A: Products (ADM-005 a ADM-012)
|
||||
├── Stream B: Orders (ADM-013 a ADM-019)
|
||||
└── Stream C: Inventory (ADM-020 a ADM-022)
|
||||
|
||||
FASE 4 — Secondary Features (paralelo)
|
||||
│
|
||||
├── Stream D: Customers (ADM-023 a ADM-025)
|
||||
├── Stream E: Categories + Brands (ADM-026 a ADM-030)
|
||||
└── Stream F: Dashboard (ADM-031)
|
||||
|
||||
FASE 5 — Extensions (paralelo)
|
||||
│
|
||||
├── Stream G: Promotions (BD-02, BD-03 + ADM-032 a ADM-034)
|
||||
├── Stream H: Reviews (BD-05 + ADM-035 a ADM-037)
|
||||
└── Stream I: CMS (ADM-038 a ADM-040)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Risk Assessment
|
||||
|
||||
| Riesgo | Severidad | Mitigación |
|
||||
|--------|-----------|------------|
|
||||
| No existe GET /auth/me | CRÍTICA | BD-01 debe implementarse antes de cualquier código de admin |
|
||||
| Promotions sin listado | ALTA | BD-02 debe existir para tener UI de promociones |
|
||||
| Reviews sin listado admin | ALTA | BD-05 debe existir para moderación |
|
||||
| Sin granularidad de roles | MEDIA | Trabajar con 'admin' vs 'customer' en frontend; backend no valida permisos finos |
|
||||
| Concurrencia de inventario | MEDIA | Siempre esperar respuesta del backend antes de mostrar nuevo stock |
|
||||
| Estado de orden inválido | MEDIA | Backend valida transiciones; UI solo muestra acciones permitidas |
|
||||
| Bulk operations | BAJA | No implementar sin endpoint de backend dedicado (BD-09) |
|
||||
Reference in New Issue
Block a user