Improve frontend 3
This commit is contained in:
@@ -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 */}
|
||||
|
||||
Reference in New Issue
Block a user