Ir al contenido

Crear Proyecto

Función principal para crear nuevos proyectos con toda su configuración inicial.

  • Componente: ProjectFormSheet.tsx
  • Ruta: Modal/Sheet desde /owner/projects
  • Botón: “Nuevo Proyecto” o FAB en móvil

El formulario está organizado en 4 tabs para mejor organización:

  • Nombre del Proyecto (requerido): Máximo 255 caracteres
  • Descripción: Texto libre para objetivos y alcance
  • Estado: Selección entre 6 estados disponibles
  • Prioridad: Baja, Media, Alta o Urgente
  • Cliente: Selección de cliente existente (opcional)
  • Color: Paleta de 10 colores para identificación visual
  • Fecha de Inicio: Selector de calendario
  • Fecha Estimada de Fin: Fecha objetivo de finalización
  • Fecha Real de Fin: Se establece cuando el proyecto se completa
  • Presupuesto Total: Monto en euros con 2 decimales
  • Se inicializa budget_used en 0
  • Sistema de Tags: Añadir múltiples etiquetas
  • Input con botón ”+” para agregar
  • Vista previa de etiquetas con opción de eliminar
  • Formato: #etiqueta

El formulario valida:

  • ✅ Nombre obligatorio (1-255 caracteres)
  • ✅ Color en formato hexadecimal válido
  • ✅ Fecha de fin posterior o igual a fecha de inicio
  • ✅ Presupuesto no negativo (0 - 999,999,999)
  • ✅ UUIDs válidos para cliente y manager
  • planning: Planificación inicial
  • active: En ejecución
  • on_hold: Pausado temporalmente
  • completed: Finalizado exitosamente
  • cancelled: Cancelado
  • archived: Archivado para referencia

Al crear un proyecto:

  1. Se inserta en la tabla projects
  2. Se inicializa con budget_used: 0 y progress: 0
  3. Se genera color automático si no se especifica
  4. Se asocia al user_id del usuario autenticado
  5. Se invalidan las queries de proyectos para refrescar la lista
Código del formulario de creación
// ProjectFormSheet.tsx - Estructura principal
const projectSchema = z.object({
name: z.string().min(1, "El nombre es obligatorio").max(255),
description: z.string().optional(),
status: z.enum(['planning', 'active', 'on_hold', 'completed', 'cancelled', 'archived']),
priority: z.enum(['low', 'medium', 'high', 'urgent']),
start_date: z.date().optional(),
end_date: z.date().optional(),
estimated_end_date: z.date().optional(),
budget_total: z.number().min(0).optional(),
client_id: z.string().optional(),
tags: z.array(z.string()).optional(),
color: z.string().min(1, "Selecciona un color"),
});
// Mutation de creación
const createProjectMutation = useMutation({
mutationFn: async (data: ProjectFormData) => {
const projectData = {
...data,
user_id: user.id,
tags: selectedTags,
budget_total: data.budget_total || 0,
start_date: data.start_date?.toISOString().split('T')[0] || null,
// ... conversión de fechas
};
const { error } = await supabase
.from('projects')
.insert([projectData]);
}
});