// types/project.ts export type ProjectStatus = '0' | '1' | '2'; // 0: borrador, 1: validado/abierto, 2: cerrado // Interface para los datos crudos que vienen de Dolibarr export interface DolibarrProject { id: string | number; ref: string; title: string; description: string; date_start: number | null; // timestamp (puede ser null) date_end: number | null; // timestamp (puede ser null) status: string; statut: string; budget_amount: string; opp_amount: string; // opportunity amount (presupuesto estimado) opp_percent: string; // porcentaje de progreso user_author_id: string | number; thirdparty_name?: string; socid?: string | number; public: string | number; usage_opportunity: string | number; usage_task: string | number; date_c: number | null; // fecha creación (puede ser null) date_m: number | null; // fecha modificación (puede ser null) } // Interface para usar en la UI (formato normalizado) export interface Project { id: number; ref: string; name: string; client: string; status: ProjectStatus; progress: number; budget: number; spent: number; startDate: string; endDate: string; description: string; createdAt: string; updatedAt: string; socid?: number; // ID del tercero/cliente } // Configuración de estados - AHORA CON FUNCIÓN QUE RETORNA LAS CLASES COMPLETAS export const STATUS_CONFIG: Record string }> = { '0': { label: "Borrador", getColorClasses: () => "bg-gray-100 text-gray-800 border-gray-200" }, '1': { label: "Abierto", getColorClasses: () => "bg-blue-100 text-blue-800 border-blue-200" }, '2': { label: "Cerrado", getColorClasses: () => "bg-green-100 text-green-800 border-green-200" } }; // Función helper para obtener las clases de color según el estado export function getStatusColorClasses(status: ProjectStatus): string { const statusMap: Record = { '0': 'bg-gray-100 text-gray-800 border-gray-200', '1': 'bg-blue-100 text-blue-800 border-blue-200', '2': 'bg-green-100 text-green-800 border-green-200' }; return statusMap[status] || 'bg-gray-100 text-gray-800 border-gray-200'; } // Función helper para convertir datos de Dolibarr a nuestro formato export function mapDolibarrProject(dolibarr: DolibarrProject): Project { const budget = parseFloat(dolibarr.budget_amount || '0'); const oppAmount = parseFloat(dolibarr.opp_amount || '0'); const progress = parseFloat(dolibarr.opp_percent || '0'); // Helper para convertir timestamp de Dolibarr (segundos) a fecha ISO const timestampToDate = (timestamp: number | null | undefined): string => { if (!timestamp || timestamp === 0) { return new Date().toISOString().split('T')[0]; // Fecha actual por defecto } return new Date(timestamp * 1000).toISOString().split('T')[0]; }; return { id: typeof dolibarr.id === 'string' ? parseInt(dolibarr.id) : dolibarr.id, ref: dolibarr.ref, name: dolibarr.title, client: dolibarr.thirdparty_name || 'Sin cliente', status: (dolibarr.status || dolibarr.statut) as ProjectStatus, progress: progress, budget: oppAmount || budget, spent: (oppAmount || budget) * (progress / 100), // Calcular gastado según progreso startDate: timestampToDate(dolibarr.date_start), endDate: timestampToDate(dolibarr.date_end), description: dolibarr.description || '', createdAt: timestampToDate(dolibarr.date_c), updatedAt: timestampToDate(dolibarr.date_m), socid: dolibarr.socid ? (typeof dolibarr.socid === 'string' ? parseInt(dolibarr.socid) : dolibarr.socid) : undefined, }; } // ==================== TIPOS PARA API DE CREAR/ACTUALIZAR ==================== /** * Datos para crear un nuevo proyecto * Campos requeridos por Dolibarr: ref, title */ export interface CreateProjectData { ref: string; // Referencia única (requerido) title: string; // Nombre del proyecto (requerido) description?: string; date_start?: number; // timestamp en segundos date_end?: number; // timestamp en segundos opp_amount?: number; // presupuesto opp_percent?: number; // progreso (0-100) budget_amount?: number; socid?: number; // ID del tercero/cliente public?: number; // 0: privado, 1: público usage_opportunity?: number; usage_task?: number; usage_bill_time?: number; usage_organize_event?: number; } /** * Datos para actualizar un proyecto existente * Todos los campos son opcionales */ export interface UpdateProjectData { ref?: string; title?: string; description?: string; date_start?: number; // timestamp en segundos date_end?: number; // timestamp en segundos opp_amount?: number; // presupuesto opp_percent?: number; // progreso (0-100) budget_amount?: number; socid?: number; // ID del tercero/cliente status?: ProjectStatus; public?: number; } /** * Interface para el formulario de proyectos (UI) * Usa formatos amigables */ export interface ProjectFormData { name: string; description: string; status: ProjectStatus; progress: number; budget: number; startDate: string; // YYYY-MM-DD endDate: string; // YYYY-MM-DD socid?: number; // ID del cliente isPublic: boolean; } // Helper para convertir fecha string a timestamp (segundos) export function dateStringToTimestamp(dateStr: string | null | undefined): number | undefined { if (!dateStr || dateStr === '') return undefined; const date = new Date(dateStr); if (isNaN(date.getTime())) return undefined; return Math.floor(date.getTime() / 1000); } // Helper para generar una referencia única para proyectos function generateProjectRef(): string { const timestamp = Date.now().toString(36).toUpperCase(); const random = Math.random().toString(36).substring(2, 6).toUpperCase(); return `PJ-${timestamp}-${random}`; } /** * Convierte datos del formulario UI al formato para crear proyecto en Dolibarr */ export function formDataToCreateProject(formData: ProjectFormData): CreateProjectData { return { ref: generateProjectRef(), title: formData.name, description: formData.description || undefined, date_start: dateStringToTimestamp(formData.startDate), date_end: dateStringToTimestamp(formData.endDate), opp_amount: formData.budget > 0 ? formData.budget : undefined, opp_percent: formData.progress, budget_amount: formData.budget > 0 ? formData.budget : undefined, socid: formData.socid || undefined, public: formData.isPublic ? 1 : 0, usage_task: 1, // Habilitar tareas por defecto usage_opportunity: 1, }; } /** * Convierte datos del formulario UI al formato para actualizar proyecto en Dolibarr */ export function formDataToUpdateProject(formData: Partial): UpdateProjectData { const updateData: UpdateProjectData = {}; if (formData.name !== undefined) updateData.title = formData.name; if (formData.description !== undefined) updateData.description = formData.description; if (formData.status !== undefined) updateData.status = formData.status; if (formData.progress !== undefined) updateData.opp_percent = formData.progress; if (formData.budget !== undefined) { updateData.opp_amount = formData.budget; updateData.budget_amount = formData.budget; } if (formData.startDate !== undefined) { updateData.date_start = dateStringToTimestamp(formData.startDate); } if (formData.endDate !== undefined) { updateData.date_end = dateStringToTimestamp(formData.endDate); } if (formData.socid !== undefined) { updateData.socid = formData.socid; } if (formData.isPublic !== undefined) { updateData.public = formData.isPublic ? 1 : 0; } return updateData; } /** * Convierte un proyecto existente a datos del formulario */ export function projectToFormData(project: Project): ProjectFormData { return { name: project.name, description: project.description || '', status: project.status, progress: project.progress, budget: project.budget, startDate: project.startDate || '', endDate: project.endDate || '', socid: project.socid, isPublic: true, // Por defecto público }; }