Improve frontend 3

This commit is contained in:
Urtzi Alfaro
2025-11-19 22:12:51 +01:00
parent 938df0866e
commit 29e6ddcea9
17 changed files with 2215 additions and 268 deletions

View File

@@ -5,7 +5,9 @@ import { statusColors } from '../../../../styles/colors';
import { formatters } from '../../../../components/ui/Stats/StatsPresets';
import { LoadingSpinner } from '../../../../components/ui';
import { PageHeader } from '../../../../components/layout';
import { ProductionSchedule, CreateProductionBatchModal, ProductionStatusCard, QualityCheckModal, ProcessStageTracker } from '../../../../components/domain/production';
import { ProductionSchedule, ProductionStatusCard, QualityCheckModal, ProcessStageTracker } from '../../../../components/domain/production';
import { UnifiedAddWizard } from '../../../../components/domain/unified-wizard';
import type { ItemType } from '../../../../components/domain/unified-wizard';
import { useCurrentTenant } from '../../../../stores/tenant.store';
import {
useProductionDashboard,
@@ -34,7 +36,7 @@ const ProductionPage: React.FC = () => {
const [priorityFilter, setPriorityFilter] = useState('');
const [selectedBatch, setSelectedBatch] = useState<ProductionBatchResponse | null>(null);
const [showBatchModal, setShowBatchModal] = useState(false);
const [showCreateModal, setShowCreateModal] = useState(false);
const [isWizardOpen, setIsWizardOpen] = useState(false);
const [showQualityModal, setShowQualityModal] = useState(false);
const [modalMode, setModalMode] = useState<'view' | 'edit'>('view');
@@ -324,7 +326,7 @@ const ProductionPage: React.FC = () => {
id: 'create-batch',
label: 'Nueva Orden de Producción',
icon: PlusCircle,
onClick: () => setShowCreateModal(true),
onClick: () => setIsWizardOpen(true),
variant: 'primary',
size: 'md'
}
@@ -420,11 +422,6 @@ const ProductionPage: React.FC = () => {
setModalMode('view');
setShowBatchModal(true);
}}
onEdit={(batch) => {
setSelectedBatch(batch);
setModalMode('edit');
setShowBatchModal(true);
}}
onStart={async (batch) => {
try {
await updateBatchStatusMutation.mutateAsync({
@@ -469,11 +466,6 @@ const ProductionPage: React.FC = () => {
setSelectedBatch(batch);
setShowQualityModal(true);
}}
onViewHistory={(batch) => {
setSelectedBatch(batch);
setModalMode('view');
setShowBatchModal(true);
}}
showDetailedProgress={true}
/>
))}
@@ -491,14 +483,14 @@ const ProductionPage: React.FC = () => {
}
actionLabel="Nueva Orden de Producción"
actionIcon={Plus}
onAction={() => setShowCreateModal(true)}
onAction={() => setIsWizardOpen(true)}
/>
)}
</>
{/* Production Batch Modal */}
{/* Production Batch Detail Modal */}
{showBatchModal && selectedBatch && (
<EditViewModal
isOpen={showBatchModal}
@@ -516,35 +508,34 @@ const ProductionPage: React.FC = () => {
text: t(`production:status.${selectedBatch.status.toLowerCase()}`),
icon: Package
}}
size="lg"
size="xl"
sections={[
{
title: 'Información General',
icon: Package,
fields: [
{
label: 'Producto',
value: selectedBatch.product_name,
highlight: true
},
{
label: 'Número de Lote',
value: selectedBatch.batch_number
},
{
label: 'Cantidad Planificada',
value: `${selectedBatch.planned_quantity} unidades`,
highlight: true
},
{
label: 'Cantidad Real',
label: 'Cantidad Producida',
value: selectedBatch.actual_quantity
? `${selectedBatch.actual_quantity} unidades`
: 'Pendiente',
editable: modalMode === 'edit',
type: 'number'
},
{
label: 'Prioridad',
value: selectedBatch.priority,
type: 'select',
editable: modalMode === 'edit',
options: Object.values(ProductionPriorityEnum).map(value => ({
value,
label: t(`production:priority.${value.toLowerCase()}`)
}))
},
{
label: 'Estado',
value: selectedBatch.status,
@@ -555,11 +546,25 @@ const ProductionPage: React.FC = () => {
label: t(`production:status.${value.toLowerCase()}`)
}))
},
{
label: 'Prioridad',
value: selectedBatch.priority,
type: 'select',
editable: modalMode === 'edit',
options: Object.values(ProductionPriorityEnum).map(value => ({
value,
label: t(`production:priority.${value.toLowerCase()}`)
}))
},
{
label: 'Personal Asignado',
value: selectedBatch.staff_assigned?.join(', ') || 'No asignado',
editable: modalMode === 'edit',
type: 'text'
},
{
label: 'Equipos Utilizados',
value: selectedBatch.equipment_used?.join(', ') || 'No especificado'
}
]
},
@@ -577,6 +582,12 @@ const ProductionPage: React.FC = () => {
value: selectedBatch.planned_end_time,
type: 'datetime'
},
{
label: 'Duración Planificada',
value: selectedBatch.planned_duration_minutes
? `${selectedBatch.planned_duration_minutes} minutos`
: 'No especificada'
},
{
label: 'Inicio Real',
value: selectedBatch.actual_start_time || 'Pendiente',
@@ -586,11 +597,17 @@ const ProductionPage: React.FC = () => {
label: 'Fin Real',
value: selectedBatch.actual_end_time || 'Pendiente',
type: 'datetime'
},
{
label: 'Duración Real',
value: selectedBatch.actual_duration_minutes
? `${selectedBatch.actual_duration_minutes} minutos`
: 'Pendiente'
}
]
},
{
title: 'Seguimiento de Proceso',
title: 'Fases del Proceso de Producción',
icon: Timer,
fields: [
{
@@ -657,7 +674,8 @@ const ProductionPage: React.FC = () => {
label: 'Puntuación de Calidad',
value: selectedBatch.quality_score
? `${selectedBatch.quality_score}/10`
: 'Pendiente'
: 'Pendiente',
highlight: selectedBatch.quality_score ? selectedBatch.quality_score >= 8 : false
},
{
label: 'Rendimiento',
@@ -674,6 +692,19 @@ const ProductionPage: React.FC = () => {
label: 'Costo Real',
value: selectedBatch.actual_cost || 0,
type: 'currency'
},
{
label: 'Notas de Producción',
value: selectedBatch.production_notes || 'Sin notas',
type: 'textarea',
editable: modalMode === 'edit',
span: 2
},
{
label: 'Notas de Calidad',
value: selectedBatch.quality_notes || 'Sin notas de calidad',
type: 'textarea',
span: 2
}
]
}
@@ -696,39 +727,33 @@ const ProductionPage: React.FC = () => {
onFieldChange={(sectionIndex, fieldIndex, value) => {
if (!selectedBatch) return;
const sections = [
['planned_quantity', 'actual_quantity', 'priority', 'status', 'staff_assigned'],
['planned_start_time', 'planned_end_time', 'actual_start_time', 'actual_end_time'],
['quality_score', 'yield_percentage', 'estimated_cost', 'actual_cost']
];
// Get the field names from modal sections
const sectionFields = [
{ fields: ['planned_quantity', 'actual_quantity', 'priority', 'status', 'staff_assigned'] },
{ fields: ['planned_start_time', 'planned_end_time', 'actual_start_time', 'actual_end_time'] },
{ fields: ['quality_score', 'yield_percentage', 'estimated_cost', 'actual_cost'] }
];
const fieldMapping: Record<string, string> = {
'Cantidad Real': 'actual_quantity',
'Prioridad': 'priority',
// General Information
'Cantidad Producida': 'actual_quantity',
'Estado': 'status',
'Personal Asignado': 'staff_assigned'
'Prioridad': 'priority',
'Personal Asignado': 'staff_assigned',
// Schedule - most fields are read-only datetime
// Quality and Costs
'Notas de Producción': 'production_notes',
'Notas de Calidad': 'quality_notes'
};
// Get section labels to map back to field names
const sectionLabels = [
['Cantidad Planificada', 'Cantidad Real', 'Prioridad', 'Estado', 'Personal Asignado'],
['Inicio Planificado', 'Fin Planificado', 'Inicio Real', 'Fin Real'],
['Puntuación de Calidad', 'Rendimiento', 'Costo Estimado', 'Costo Real']
['Producto', 'Número de Lote', 'Cantidad Planificada', 'Cantidad Producida', 'Estado', 'Prioridad', 'Personal Asignado', 'Equipos Utilizados'],
['Inicio Planificado', 'Fin Planificado', 'Duración Planificada', 'Inicio Real', 'Fin Real', 'Duración Real'],
[], // Process Stage Tracker section - no editable fields
['Puntuación de Calidad', 'Rendimiento', 'Costo Estimado', 'Costo Real', 'Notas de Producción', 'Notas de Calidad']
];
const fieldLabel = sectionLabels[sectionIndex]?.[fieldIndex];
const propertyName = fieldMapping[fieldLabel] || sectionFields[sectionIndex]?.fields[fieldIndex];
const propertyName = fieldMapping[fieldLabel];
if (propertyName) {
let processedValue: any = value;
// Process specific field types
if (propertyName === 'staff_assigned' && typeof value === 'string') {
processedValue = value.split(',').map(s => s.trim()).filter(s => s.length > 0);
} else if (propertyName === 'actual_quantity') {
@@ -744,11 +769,15 @@ const ProductionPage: React.FC = () => {
/>
)}
{/* Create Production Batch Modal */}
<CreateProductionBatchModal
isOpen={showCreateModal}
onClose={() => setShowCreateModal(false)}
onCreateBatch={handleCreateBatch}
{/* Unified Add Wizard for Production Batches */}
<UnifiedAddWizard
isOpen={isWizardOpen}
onClose={() => setIsWizardOpen(false)}
onComplete={(itemType: ItemType, data?: any) => {
console.log('Production batch created:', data);
refetchBatches();
}}
initialItemType="production-batch"
/>
{/* Quality Check Modal */}