Genera un módulo completo en GPMS (backend service + router routes + React page + i18n keys + App/Layout/Sidebar wiring). Úsalo cuando el usuario quiera crear un nuevo módulo/entidad en el sistema.
Instal·la
npx skillscat add gustavovillen/cms3-0/scaffold-module Instal·la des del registre de SkillsCat.
Genera un módulo completo para GPMS. El nombre del módulo es: $ARGUMENTS
Sigue estos pasos en orden. Lee los archivos necesarios antes de modificarlos.
1. Analiza el contexto
- Lee
apps/api/src/tenant/spare-requests/spare-requests-service.tscomo referencia del patrón de servicio. - Lee
apps/api/src/tenant/pms/spares-router.tspara ver cómo se agregan rutas. - Lee
apps/web-modern/src/pages/SpareRequests.tsxcomo referencia del patrón de página. - Lee
apps/web-modern/src/lib/i18n.tsxpara ver la estructura del dict. - Lee
apps/web-modern/src/App.tsx,apps/web-modern/src/components/Layout.tsx,apps/web-modern/src/components/Sidebar.tsx.
2. Deriva los nombres
Del argumento $ARGUMENTS (ej: "purchase-orders", "inspections", "certificates"):
- kebab: el argumento tal cual (ej:
purchase-orders) - camelPlural: versión camelCase plural (ej:
purchaseOrders) - PascalSingular: versión PascalCase singular (ej:
PurchaseOrder) - PascalPlural: versión PascalCase plural (ej:
PurchaseOrders) - path: la ruta URL (ej:
/purchase-orders) - i18nPrefix: prefijo para claves i18n (ej:
po)
3. Crea el servicio backend
Crea apps/api/src/tenant/$ARGUMENTS/$ARGUMENTS-service.ts con:
- Función
resolveTenantId(session)— misma que en spare-requests-service.ts list${PascalPlural}(session, filters)— conprisma.findMany,where: { tenantId, deletedAt: null }get${PascalSingular}(session, id)— confindFirsty throw 404create${PascalSingular}(session, input)— concanManageguard yvoid publishAudit(...)update${PascalSingular}(session, id, input)— concanManageguarddelete${PascalSingular}(session, id)— soft delete (deletedAt)- Imports:
TenantAccessSession,getPrismaClient,RouteError,publishAudit canManage: rolesTENANT_ADMIN,MAINTENANCE_MANAGER,PROCUREMENT_STORE
4. Agrega rutas al router
En apps/api/src/tenant/pms/spares-router.ts:
- Agrega el import del nuevo servicio
- Agrega
/app/pms/$ARGUMENTSal prefijo guard (condiciónurl.pathname.startsWith(...)) - Agrega rutas:
GET /app/pms/$ARGUMENTS,POST /app/pms/$ARGUMENTS,GET/PATCH/DELETE /app/pms/$ARGUMENTS/:id
5. Crea la página React
Crea apps/web-modern/src/pages/${PascalPlural}.tsx con:
- Interfaz TypeScript para la entidad
useFetchpara cargar datos del endpoint/app/pms/$ARGUMENTSPageHeadercon botón "Nuevo" usandoPlusiconDataTablecononRowClick={row => setEditing(row)}(NUNCA botón Editar en la fila)- Modal con:
- Header: título + botón X
- Body: formulario con campos (inferidos del modelo Prisma si existe, o campos genéricos)
- Footer: botón "Cerrar" + botón "Guardar" (solo si aplica)
- Estado:
editing: Entity | null | "new" fetchconcredentials: "include"para POST/PATCH
6. Agrega claves i18n
En apps/web-modern/src/lib/i18n.tsx, dentro del dict, agrega una sección con prefijo ${i18nPrefix}:
nav.${camelPlural}: nombre en sidebar (es/en/pt)${i18nPrefix}.new: "Nuevo X" / "New X" / "Novo X"${i18nPrefix}.name: nombre del campo principal${i18nPrefix}.status: "Estado" / "Status" / "Status" (si aplica)empty.${camelPlural}: mensaje de lista vacía
7. Conecta en App.tsx
En apps/web-modern/src/App.tsx:
- Agrega el import de
${PascalPlural}Page - Agrega la ruta:
<Route path="/$ARGUMENTS" element={<${PascalPlural}Page />} />
8. Conecta en Layout.tsx
En apps/web-modern/src/components/Layout.tsx, en el objeto TITLES:
- Agrega:
"/$ARGUMENTS": "Título legible en español"
9. Conecta en Sidebar.tsx
En apps/web-modern/src/components/Sidebar.tsx, en MAIN_ITEMS:
- Agrega el item con el ícono más apropiado de lucide-react
- Si el ícono no está importado, agrégalo al import
10. Verifica TypeScript
Corre: npx tsc -p apps/api/tsconfig.json --noEmit 2>&1 | grep "$ARGUMENTS" para verificar errores en el nuevo servicio.
Corre: npx tsc -p apps/web-modern/tsconfig.json --noEmit 2>&1 | grep "${PascalPlural}" para verificar la página.
Reglas invariantes
- NUNCA escribir
currentStockni leer stock desde campos directos — siempre usarstock-calc-service - NUNCA agregar botón "Editar" en filas de tabla — el click en la fila abre el modal
- Todos los
fetchdeben usarcredentials: "include" publishAuditsiempre convoid(fire-and-forget)- Soft delete:
deletedAt+deletedByUserId, nuncaprisma.delete