Edifiko
Edifiko DocsDocumentación del proyecto
  • Overview
Proyecto
  • README
  • CONTRIBUTING
  • WORKFLOW
  • Workflows CI/CD
  • UI Components
  • Showcase: Works UI Components
  • Schemas
  • Servicios
  • Hooks
  • Utils
  • Types
  • Constants
  • Errors
  • API Materiales
Herramientas de IA
  • Agentes
  • Skills
  1. Documentación
  2. Schemas

Schemas

schemas/README.md

schemas/

Fuente única de verdad (SSOT) para la validación de datos, tipado y contratos de formularios de Edifiko. Todos los esquemas están construidos con Zod y organizados por dominio de negocio.


Arquitectura y convenciones

ReglaDetalle
Zod como SSOTToda entidad de BD o payload de formulario nace de un esquema Zod. Los tipos de TypeScript se infieren con z.infer<typeof schema>.
camelCase para constLas variables de esquema usan camelCase (userSchema, categoryRecordSchema). Enforceado por ESLint @typescript-eslint/naming-convention.
PascalCase para typeLos tipos inferidos usan PascalCase (User, CategoryRecord).
Tests obligatoriosCada schema tiene su archivo .test.ts adyacente al schema.
Mensajes en españolLos mensajes de error de Zod visibles al usuario deben estar en español.
Un folder por dominioCada colección de Firestore tiene su propia carpeta dentro de schemas/.

collections.ts

Archivo raíz con las utilidades compartidas por todos los módulos.

COLLECTION_PATHS

Builders de rutas de documentos Firestore, tipados con los nombres de colección de constants/firebase.constants.ts.

ts
import { COLLECTION_PATHS } from '@/schemas/collections';

COLLECTION_PATHS.user('uid-123'); // → 'users/uid-123'
COLLECTION_PATHS.category('cat-456'); // → 'categories/cat-456'
COLLECTION_PATHS.material('mat-789'); // → 'materials/mat-789'
COLLECTION_PATHS.supplierCatalogItem('x'); // → 'supplier_catalog/x'
COLLECTION_PATHS.project('proj-1'); // → 'projects/proj-1'
COLLECTION_PATHS.projectMaterial('pm-1'); // → 'project_materials/pm-1'
COLLECTION_PATHS.quote('q-1'); // → 'quotes/q-1'
COLLECTION_PATHS.company('co-1'); // → 'companies/co-1'

baseDocumentSchema

Campos generados por Firestore, siempre presentes en documentos leídos de la BD. Se extiende con .extend() para crear los RecordSchema de cada módulo.

ts
{
  id: z.string().min(1); // ID del documento
  path: z.string().min(1); // Ruta completa del documento
  createdAt: z.date();
  updatedAt: z.date();
}

Módulos implementados

users/

Colección Firestore: /users/{userId}

ExportDescripción
USER_ROLESArray as const con los 6 roles del sistema
userRoleSchemaz.enum(USER_ROLES)
baseUserSchemaCampos de escritura: email, name, role, companyId
userSchemabaseUserSchema + baseDocumentSchema — registro completo de BD
createUserSchemaAlias de baseUserSchema (sin campos de BD)
updateUserSchemauserSchema.partial() — todos los campos opcionales

Roles disponibles: admin · architect_owner · architect_employee · supplier_owner · supplier_sales · supplier_logistics


categories/

Colección Firestore: /categories/{categoryId}
Este módulo gestiona la jerarquía completa de 3 niveles: Rubro → Categoría → Subcategoría.

Rubro (IGroup)

ExportDescripción
baseGroupSchematitle (2–50), description? (máx 120), iconName, isActive
groupSchemabaseGroupSchema + baseDocumentSchema
createGroupSchemaAlias de baseGroupSchema
updateGroupSchemagroupSchema.partial()

Categoría (ICategory)

ExportDescripción
baseCategorySchemagroupId, name (2–80), isActive
categorySchemabaseCategorySchema + baseDocumentSchema
createCategorySchemaAlias de baseCategorySchema
updateCategorySchemacategorySchema.partial()

Subcategoría (ISubcategory)

ExportDescripción
baseSubcategorySchemacategoryId, name (2–80), isActive
subcategorySchemabaseSubcategorySchema + baseDocumentSchema
createSubcategorySchemaAlias de baseSubcategorySchema
updateSubcategorySchemasubcategorySchema.partial()

materials/

Colección Firestore: /materials/{materialId}

ExportDescripción
MATERIAL_UNITSArray as const con las unidades válidas del sistema
materialUnitSchemaz.enum(MATERIAL_UNITS)
baseMaterialSchemaCampos de escritura: name, subcategoryId, categoryId, groupId, groupName?, categoryName?, subcategoryName?, isActive
materialSchemabaseMaterialSchema + baseDocumentSchema — registro completo de BD
createMaterialSchemaAlias de baseMaterialSchema
updateMaterialSchemabaseMaterialSchema.partial()

Campos de categorización desnormalizados:

Además del subcategoryId (FK), el schema almacena los nombres de los tres niveles de la jerarquía para evitar joins en lectura:

CampoTipoRequeridoDescripción
groupIdstring✅ID del rubro al que pertenece
categoryIdstring✅ID de la categoría al que pertenece
subcategoryIdstring✅ID de la subcategoría
groupNamestring?❌Nombre del rubro (desnormalizado)
categoryNamestring?❌Nombre de la categoría (desnorm.)
subcategoryNamestring?❌Nombre de la subcategoría (desnorm.)

Unidades disponibles: unidad · bolsa · kg · tonelada · litro · metro · m2 · m3


Módulos pendientes (carpetas vacías)

CarpetaColección FirestoreEstado
companies//companies/{companyId}Pendiente
products//supplier_catalog/{itemId}Pendiente
projects//projects/{projectId}Pendiente
lists//project_materials/{itemId}Pendiente
quotes//quotes/{quoteId}Pendiente

Cómo agregar un nuevo schema

  1. Crear la carpeta schemas/{dominio}/.
  2. Crear {dominio}.schema.ts siguiendo el patrón:
ts
import { z } from 'zod';
import { baseDocumentSchema } from '@/schemas/collections';

export const baseFooSchema = z.object({
  // campos de escritura (CREATE / UPDATE)
});
export type IFoo = z.infer<typeof baseFooSchema>;

export const fooSchema = z.object({
  ...baseFooSchema.shape,
  ...baseDocumentSchema.shape,
});
export type IFooRecord = z.infer<typeof fooSchema>;

export const createFooSchema = baseFooSchema;
export type ICreateFoo = z.infer<typeof createFooSchema>;

export const updateFooSchema = baseFooSchema.partial();
export type IUpdateFoo = z.infer<typeof updateFooSchema>;
  1. Crear {dominio}.schema.test.ts con casos de éxito y fallo para cada campo.
  2. Ejecutar pnpm test y pnpm lint para verificar.