Add improved production UI 3
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import { Plus, Clock, AlertCircle, CheckCircle, Timer, ChefHat, Eye, Edit, Package } from 'lucide-react';
|
||||
import { Button, Input, Card, StatsGrid, StatusModal } from '../../../../components/ui';
|
||||
import { Plus, Clock, AlertCircle, CheckCircle, Timer, ChefHat, Eye, Edit, Package, Zap, User, PlusCircle } from 'lucide-react';
|
||||
import { Button, Input, Card, StatsGrid, StatusModal, Toggle } from '../../../../components/ui';
|
||||
import { statusColors } from '../../../../styles/colors';
|
||||
import { formatters } from '../../../../components/ui/Stats/StatsPresets';
|
||||
import { LoadingSpinner } from '../../../../components/shared';
|
||||
import { PageHeader } from '../../../../components/layout';
|
||||
import { ProductionSchedule, BatchTracker, QualityDashboard, EquipmentManager, CreateProductionBatchModal, ProductionStatusCard, QualityCheckModal } from '../../../../components/domain/production';
|
||||
import { ProductionSchedule, BatchTracker, QualityDashboard, EquipmentManager, CreateProductionBatchModal, ProductionStatusCard, QualityCheckModal, ProcessStageTracker, CompactProcessStageTracker } from '../../../../components/domain/production';
|
||||
import { useCurrentTenant } from '../../../../stores/tenant.store';
|
||||
import {
|
||||
useProductionDashboard,
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
ProductionPriorityEnum
|
||||
} from '../../../../api';
|
||||
import { useProductionEnums } from '../../../../utils/enumHelpers';
|
||||
import { ProcessStage } from '../../../../api/types/qualityTemplates';
|
||||
|
||||
const ProductionPage: React.FC = () => {
|
||||
const [activeTab, setActiveTab] = useState('schedule');
|
||||
@@ -33,6 +34,7 @@ const ProductionPage: React.FC = () => {
|
||||
const [showCreateModal, setShowCreateModal] = useState(false);
|
||||
const [showQualityModal, setShowQualityModal] = useState(false);
|
||||
const [modalMode, setModalMode] = useState<'view' | 'edit'>('view');
|
||||
const [isAIMode, setIsAIMode] = useState(true);
|
||||
|
||||
const currentTenant = useCurrentTenant();
|
||||
const tenantId = currentTenant?.id || '';
|
||||
@@ -68,6 +70,133 @@ const ProductionPage: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// Stage management handlers
|
||||
const handleStageAdvance = async (batchId: string, currentStage: ProcessStage) => {
|
||||
const stages = Object.values(ProcessStage);
|
||||
const currentIndex = stages.indexOf(currentStage);
|
||||
const nextStage = stages[currentIndex + 1];
|
||||
|
||||
if (nextStage) {
|
||||
try {
|
||||
await updateBatchStatusMutation.mutateAsync({
|
||||
batchId,
|
||||
updates: {
|
||||
current_process_stage: nextStage,
|
||||
process_stage_history: {
|
||||
[currentStage]: { end_time: new Date().toISOString() },
|
||||
[nextStage]: { start_time: new Date().toISOString() }
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error advancing stage:', error);
|
||||
}
|
||||
} else {
|
||||
// Final stage - mark as completed
|
||||
await updateBatchStatusMutation.mutateAsync({
|
||||
batchId,
|
||||
updates: { status: ProductionStatusEnum.COMPLETED }
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleStageStart = async (batchId: string, stage: ProcessStage) => {
|
||||
try {
|
||||
await updateBatchStatusMutation.mutateAsync({
|
||||
batchId,
|
||||
updates: {
|
||||
status: ProductionStatusEnum.IN_PROGRESS,
|
||||
current_process_stage: stage,
|
||||
process_stage_history: {
|
||||
[stage]: { start_time: new Date().toISOString() }
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error starting stage:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleQualityCheckForStage = (batch: ProductionBatchResponse, stage: ProcessStage) => {
|
||||
setSelectedBatch(batch);
|
||||
setShowQualityModal(true);
|
||||
// The QualityCheckModal should be enhanced to handle stage-specific checks
|
||||
};
|
||||
|
||||
// Helper function to generate mock process stage data for the selected batch
|
||||
const generateMockProcessStageData = (batch: ProductionBatchResponse) => {
|
||||
// Mock data based on batch status - this would come from the API in real implementation
|
||||
const mockProcessStage = {
|
||||
current: batch.status === ProductionStatusEnum.PENDING ? 'mixing' as const :
|
||||
batch.status === ProductionStatusEnum.IN_PROGRESS ? 'baking' as const :
|
||||
batch.status === ProductionStatusEnum.QUALITY_CHECK ? 'cooling' as const :
|
||||
'finishing' as const,
|
||||
history: batch.status !== ProductionStatusEnum.PENDING ? [
|
||||
{ stage: 'mixing' as const, timestamp: batch.actual_start_time || batch.planned_start_time, duration: 30 },
|
||||
...(batch.status === ProductionStatusEnum.IN_PROGRESS || batch.status === ProductionStatusEnum.QUALITY_CHECK || batch.status === ProductionStatusEnum.COMPLETED ? [
|
||||
{ stage: 'proofing' as const, timestamp: new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString(), duration: 90 },
|
||||
{ stage: 'shaping' as const, timestamp: new Date(Date.now() - 1 * 60 * 60 * 1000).toISOString(), duration: 15 }
|
||||
] : []),
|
||||
...(batch.status === ProductionStatusEnum.QUALITY_CHECK || batch.status === ProductionStatusEnum.COMPLETED ? [
|
||||
{ stage: 'baking' as const, timestamp: new Date(Date.now() - 30 * 60 * 1000).toISOString(), duration: 45 }
|
||||
] : [])
|
||||
] : [],
|
||||
pendingQualityChecks: batch.status === ProductionStatusEnum.IN_PROGRESS ? [
|
||||
{
|
||||
id: 'qc1',
|
||||
name: 'Control de temperatura interna',
|
||||
stage: 'baking' as const,
|
||||
isRequired: true,
|
||||
isCritical: true,
|
||||
status: 'pending' as const,
|
||||
checkType: 'temperature' as const
|
||||
}
|
||||
] : batch.status === ProductionStatusEnum.QUALITY_CHECK ? [
|
||||
{
|
||||
id: 'qc2',
|
||||
name: 'Inspección visual final',
|
||||
stage: 'cooling' as const,
|
||||
isRequired: true,
|
||||
isCritical: false,
|
||||
status: 'pending' as const,
|
||||
checkType: 'visual' as const
|
||||
}
|
||||
] : [],
|
||||
completedQualityChecks: batch.status === ProductionStatusEnum.COMPLETED ? [
|
||||
{
|
||||
id: 'qc1',
|
||||
name: 'Control de temperatura interna',
|
||||
stage: 'baking' as const,
|
||||
isRequired: true,
|
||||
isCritical: true,
|
||||
status: 'completed' as const,
|
||||
checkType: 'temperature' as const
|
||||
},
|
||||
{
|
||||
id: 'qc2',
|
||||
name: 'Inspección visual final',
|
||||
stage: 'cooling' as const,
|
||||
isRequired: true,
|
||||
isCritical: false,
|
||||
status: 'completed' as const,
|
||||
checkType: 'visual' as const
|
||||
}
|
||||
] : batch.status === ProductionStatusEnum.IN_PROGRESS ? [
|
||||
{
|
||||
id: 'qc3',
|
||||
name: 'Verificación de masa',
|
||||
stage: 'mixing' as const,
|
||||
isRequired: true,
|
||||
isCritical: false,
|
||||
status: 'completed' as const,
|
||||
checkType: 'visual' as const
|
||||
}
|
||||
] : []
|
||||
};
|
||||
|
||||
return mockProcessStage;
|
||||
};
|
||||
|
||||
|
||||
const batches = activeBatchesData?.batches || [];
|
||||
|
||||
@@ -139,19 +268,55 @@ const ProductionPage: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<PageHeader
|
||||
title="Gestión de Producción"
|
||||
description="Planifica y controla la producción diaria de tu panadería"
|
||||
actions={[
|
||||
{
|
||||
id: "new",
|
||||
label: "Nueva Orden de Producción",
|
||||
variant: "primary" as const,
|
||||
icon: Plus,
|
||||
onClick: () => setShowCreateModal(true)
|
||||
}
|
||||
]}
|
||||
/>
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<PageHeader
|
||||
title="Gestión de Producción"
|
||||
description="Planifica y controla la producción diaria de tu panadería"
|
||||
/>
|
||||
<div className="flex items-center gap-4">
|
||||
{/* AI/Manual Mode Segmented Control */}
|
||||
<div className="inline-flex p-1 bg-[var(--surface-secondary)] rounded-xl border border-[var(--border-primary)] shadow-sm">
|
||||
<button
|
||||
onClick={() => setIsAIMode(true)}
|
||||
className={`
|
||||
flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200 ease-in-out
|
||||
${isAIMode
|
||||
? 'bg-[var(--color-primary)] text-white shadow-sm'
|
||||
: 'text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--surface-tertiary)]'
|
||||
}
|
||||
`}
|
||||
>
|
||||
<Zap className="w-4 h-4" />
|
||||
Automático IA
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setIsAIMode(false)}
|
||||
className={`
|
||||
flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200 ease-in-out
|
||||
${!isAIMode
|
||||
? 'bg-[var(--color-primary)] text-white shadow-sm'
|
||||
: 'text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--surface-tertiary)]'
|
||||
}
|
||||
`}
|
||||
>
|
||||
<User className="w-4 h-4" />
|
||||
Manual
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{!isAIMode && (
|
||||
<Button
|
||||
variant="primary"
|
||||
size="md"
|
||||
onClick={() => setShowCreateModal(true)}
|
||||
className="flex items-center gap-2.5 px-6 py-3 text-sm font-semibold tracking-wide shadow-lg hover:shadow-xl transition-all duration-200"
|
||||
>
|
||||
<PlusCircle className="w-5 h-5" />
|
||||
Nueva Orden de Producción
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Production Stats */}
|
||||
<StatsGrid
|
||||
@@ -209,26 +374,6 @@ const ProductionPage: React.FC = () => {
|
||||
>
|
||||
Programación
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('quality-dashboard')}
|
||||
className={`py-2 px-1 border-b-2 font-medium text-sm ${
|
||||
activeTab === 'quality-dashboard'
|
||||
? 'border-orange-500 text-[var(--color-primary)]'
|
||||
: 'border-transparent text-[var(--text-tertiary)] hover:text-[var(--text-secondary)] hover:border-[var(--border-secondary)]'
|
||||
}`}
|
||||
>
|
||||
Dashboard Calidad
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('equipment')}
|
||||
className={`py-2 px-1 border-b-2 font-medium text-sm ${
|
||||
activeTab === 'equipment'
|
||||
? 'border-orange-500 text-[var(--color-primary)]'
|
||||
: 'border-transparent text-[var(--text-tertiary)] hover:text-[var(--text-secondary)] hover:border-[var(--border-secondary)]'
|
||||
}`}
|
||||
>
|
||||
Equipos
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
@@ -342,13 +487,6 @@ const ProductionPage: React.FC = () => {
|
||||
)}
|
||||
|
||||
|
||||
{activeTab === 'quality-dashboard' && (
|
||||
<QualityDashboard />
|
||||
)}
|
||||
|
||||
{activeTab === 'equipment' && (
|
||||
<EquipmentManager />
|
||||
)}
|
||||
|
||||
{/* Production Batch Modal */}
|
||||
{showBatchModal && selectedBatch && (
|
||||
@@ -436,36 +574,23 @@ const ProductionPage: React.FC = () => {
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Etapas de Producción',
|
||||
title: 'Seguimiento de Proceso',
|
||||
icon: Timer,
|
||||
fields: [
|
||||
{
|
||||
label: 'Etapa Actual',
|
||||
value: selectedBatch.status === ProductionStatusEnum.IN_PROGRESS
|
||||
? 'En progreso'
|
||||
: selectedBatch.status === ProductionStatusEnum.QUALITY_CHECK
|
||||
? 'Control de calidad'
|
||||
: productionEnums.getProductionStatusLabel(selectedBatch.status)
|
||||
},
|
||||
{
|
||||
label: 'Progreso del Lote',
|
||||
value: selectedBatch.status === ProductionStatusEnum.COMPLETED
|
||||
? '100%'
|
||||
: selectedBatch.status === ProductionStatusEnum.PENDING
|
||||
? '0%'
|
||||
: '50%'
|
||||
},
|
||||
{
|
||||
label: 'Tiempo Transcurrido',
|
||||
value: selectedBatch.actual_start_time
|
||||
? `${Math.round((new Date().getTime() - new Date(selectedBatch.actual_start_time).getTime()) / (1000 * 60))} minutos`
|
||||
: 'No iniciado'
|
||||
},
|
||||
{
|
||||
label: 'Tiempo Estimado Restante',
|
||||
value: selectedBatch.planned_end_time && selectedBatch.actual_start_time
|
||||
? `${Math.max(0, Math.round((new Date(selectedBatch.planned_end_time).getTime() - new Date().getTime()) / (1000 * 60)))} minutos`
|
||||
: 'Calculando...'
|
||||
label: '',
|
||||
value: (
|
||||
<CompactProcessStageTracker
|
||||
processStage={generateMockProcessStageData(selectedBatch)}
|
||||
onAdvanceStage={(currentStage) => handleStageAdvance(selectedBatch.id, currentStage)}
|
||||
onQualityCheck={(checkId) => {
|
||||
setShowQualityModal(true);
|
||||
console.log('Opening quality check:', checkId);
|
||||
}}
|
||||
className="w-full"
|
||||
/>
|
||||
),
|
||||
span: 2
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user