import React, { useState, useMemo } from 'react'; import { Plus, Search, Filter, Edit, Copy, Trash2, Eye, CheckCircle, AlertTriangle, Settings, Tag, Thermometer, Scale, Timer, FileCheck } from 'lucide-react'; import { Button, Badge, StatusCard, Modal, StatsGrid, SearchAndFilter, type FilterConfig } from '../../ui'; import { LoadingSpinner } from '../../ui'; import { PageHeader } from '../../layout'; import { useCurrentTenant } from '../../../stores/tenant.store'; import { useQualityTemplates, useCreateQualityTemplate, useUpdateQualityTemplate, useDeleteQualityTemplate, useDuplicateQualityTemplate } from '../../../api/hooks/qualityTemplates'; import { QualityCheckType, ProcessStage, type QualityCheckTemplate, type QualityCheckTemplateCreate, type QualityCheckTemplateUpdate } from '../../../api/types/qualityTemplates'; import { CreateQualityTemplateModal } from './CreateQualityTemplateModal'; import { EditQualityTemplateModal } from './EditQualityTemplateModal'; import { ViewQualityTemplateModal } from './ViewQualityTemplateModal'; import { useTranslation } from 'react-i18next'; interface QualityTemplateManagerProps { className?: string; } const QUALITY_CHECK_TYPE_CONFIG = { [QualityCheckType.VISUAL]: { icon: Eye, label: 'Visual', color: 'bg-blue-500', description: 'Inspección visual' }, [QualityCheckType.MEASUREMENT]: { icon: Settings, label: 'Medición', color: 'bg-green-500', description: 'Mediciones precisas' }, [QualityCheckType.TEMPERATURE]: { icon: Thermometer, label: 'Temperatura', color: 'bg-red-500', description: 'Control de temperatura' }, [QualityCheckType.WEIGHT]: { icon: Scale, label: 'Peso', color: 'bg-purple-500', description: 'Control de peso' }, [QualityCheckType.BOOLEAN]: { icon: CheckCircle, label: 'Sí/No', color: 'bg-gray-500', description: 'Verificación binaria' }, [QualityCheckType.TIMING]: { icon: Timer, label: 'Tiempo', color: 'bg-orange-500', description: 'Control de tiempo' }, [QualityCheckType.CHECKLIST]: { icon: FileCheck, label: 'Lista de verificación', color: 'bg-indigo-500', description: 'Checklist de verificación' } }; const PROCESS_STAGE_LABELS = { [ProcessStage.MIXING]: 'Mezclado', [ProcessStage.PROOFING]: 'Fermentación', [ProcessStage.SHAPING]: 'Formado', [ProcessStage.BAKING]: 'Horneado', [ProcessStage.COOLING]: 'Enfriado', [ProcessStage.PACKAGING]: 'Empaquetado', [ProcessStage.FINISHING]: 'Acabado' }; export const QualityTemplateManager: React.FC = ({ className = '' }) => { const { t } = useTranslation(); const [searchTerm, setSearchTerm] = useState(''); const [selectedCheckType, setSelectedCheckType] = useState(''); const [selectedStage, setSelectedStage] = useState(''); const [showActiveOnly, setShowActiveOnly] = useState(true); const [showCreateModal, setShowCreateModal] = useState(false); const [showEditModal, setShowEditModal] = useState(false); const [showViewModal, setShowViewModal] = useState(false); const [selectedTemplate, setSelectedTemplate] = useState(null); const currentTenant = useCurrentTenant(); const tenantId = currentTenant?.id || ''; // Helper function to get translated category label const getCategoryLabel = (category: string | null | undefined): string => { if (!category) return t('production.quality.categories.appearance', 'Sin categoría'); const translationKey = `production.quality.categories.${category}`; const translated = t(translationKey); // If translation is same as key, it means no translation exists, return the original return translated === translationKey ? category : translated; }; // API hooks const { data: templatesData, isLoading, error } = useQualityTemplates(tenantId, { check_type: selectedCheckType || undefined, stage: selectedStage || undefined, is_active: showActiveOnly, search: searchTerm || undefined }); const createTemplateMutation = useCreateQualityTemplate(tenantId); const updateTemplateMutation = useUpdateQualityTemplate(tenantId); const deleteTemplateMutation = useDeleteQualityTemplate(tenantId); const duplicateTemplateMutation = useDuplicateQualityTemplate(tenantId); // Filtered templates const filteredTemplates = useMemo(() => { if (!templatesData?.templates) return []; return templatesData.templates.filter(template => { const matchesSearch = !searchTerm || template.name.toLowerCase().includes(searchTerm.toLowerCase()) || template.description?.toLowerCase().includes(searchTerm.toLowerCase()) || template.category?.toLowerCase().includes(searchTerm.toLowerCase()); return matchesSearch; }); }, [templatesData?.templates, searchTerm]); // Statistics const templateStats = useMemo(() => { const templates = templatesData?.templates || []; return { total: templates.length, active: templates.filter(t => t.is_active).length, critical: templates.filter(t => t.is_critical).length, required: templates.filter(t => t.is_required).length, byType: Object.values(QualityCheckType).map(type => ({ type, count: templates.filter(t => t.check_type === type).length })) }; }, [templatesData?.templates]); // Event handlers const handleCreateTemplate = async (templateData: QualityCheckTemplateCreate) => { try { await createTemplateMutation.mutateAsync(templateData); setShowCreateModal(false); } catch (error) { console.error('Error creating template:', error); } }; const handleUpdateTemplate = async (templateData: QualityCheckTemplateUpdate) => { if (!selectedTemplate) return; try { await updateTemplateMutation.mutateAsync({ templateId: selectedTemplate.id, templateData }); setShowEditModal(false); setSelectedTemplate(null); } catch (error) { console.error('Error updating template:', error); } }; const handleDeleteTemplate = async (templateId: string) => { if (!confirm('¿Estás seguro de que quieres eliminar esta plantilla?')) return; try { await deleteTemplateMutation.mutateAsync(templateId); } catch (error) { console.error('Error deleting template:', error); } }; const handleDuplicateTemplate = async (templateId: string) => { try { await duplicateTemplateMutation.mutateAsync(templateId); } catch (error) { console.error('Error duplicating template:', error); } }; const getTemplateStatusConfig = (template: QualityCheckTemplate) => { const typeConfig = QUALITY_CHECK_TYPE_CONFIG[template.check_type]; return { color: template.is_active ? typeConfig.color : '#6b7280', text: typeConfig.label, icon: typeConfig.icon }; }; if (isLoading) { return (
); } if (error) { return (

Error al cargar las plantillas

{error?.message || 'Ha ocurrido un error inesperado'}

); } return (
setShowCreateModal(true) } ]} /> {/* Statistics */} {/* Search and Filter Controls */} setSelectedCheckType(value as QualityCheckType | ''), placeholder: 'Todos los tipos', options: Object.entries(QUALITY_CHECK_TYPE_CONFIG).map(([type, config]) => ({ value: type, label: config.label })) }, { key: 'stage', label: 'Etapa del proceso', type: 'dropdown', value: selectedStage, onChange: (value) => setSelectedStage(value as ProcessStage | ''), placeholder: 'Todas las etapas', options: Object.entries(PROCESS_STAGE_LABELS).map(([stage, label]) => ({ value: stage, label: label })) }, { key: 'activeOnly', label: 'Solo activas', type: 'checkbox', value: showActiveOnly, onChange: (value) => setShowActiveOnly(value as boolean) } ] as FilterConfig[]} /> {/* Templates Grid */}
{filteredTemplates.map((template) => { const statusConfig = getTemplateStatusConfig(template); const stageLabels = template.applicable_stages?.map(stage => PROCESS_STAGE_LABELS[stage] ) || ['Todas las etapas']; return ( 2 ? `${stageLabels.slice(0, 2).join(', ')}...` : stageLabels.join(', ') }} metadata={[ template.description || 'Sin descripción', `${template.is_required ? 'Requerido' : 'Opcional'}`, `${template.is_critical ? 'Crítico' : 'Normal'}` ]} actions={[ { label: 'Ver', icon: Eye, variant: 'primary', priority: 'primary', onClick: () => { setSelectedTemplate(template); setShowViewModal(true); } }, { label: 'Editar', icon: Edit, priority: 'secondary', onClick: () => { setSelectedTemplate(template); setShowEditModal(true); } }, { label: 'Duplicar', icon: Copy, priority: 'secondary', onClick: () => handleDuplicateTemplate(template.id) }, { label: 'Eliminar', icon: Trash2, destructive: true, priority: 'secondary', onClick: () => handleDeleteTemplate(template.id) } ]} /> ); })}
{/* Empty State */} {filteredTemplates.length === 0 && (

No se encontraron plantillas

{templatesData?.templates?.length === 0 ? 'No hay plantillas de calidad creadas. Crea la primera plantilla para comenzar.' : 'Intenta ajustar los filtros de búsqueda o crear una nueva plantilla' }

)} {/* Create Template Modal */} setShowCreateModal(false)} onCreateTemplate={handleCreateTemplate} isLoading={createTemplateMutation.isPending} /> {/* Edit Template Modal */} {selectedTemplate && ( { setShowEditModal(false); setSelectedTemplate(null); }} template={selectedTemplate} onUpdateTemplate={handleUpdateTemplate} isLoading={updateTemplateMutation.isPending} /> )} {/* View Template Modal */} {selectedTemplate && ( { setShowViewModal(false); setSelectedTemplate(null); }} template={selectedTemplate} onEdit={() => { setShowViewModal(false); setShowEditModal(true); }} /> )}
); }; export default QualityTemplateManager;