Add new page designs
This commit is contained in:
@@ -1,22 +1,15 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Plus, Calendar, Clock, Users, AlertCircle, Search, Download, Filter } from 'lucide-react';
|
||||
import { Button, Card, Badge } from '../../../../components/ui';
|
||||
import { DataTable } from '../../../../components/shared';
|
||||
import type { DataTableColumn, DataTableFilter, DataTablePagination, DataTableSelection } from '../../../../components/shared';
|
||||
import { Plus, Download, Clock, Users, AlertCircle, CheckCircle, Timer, ChefHat, Eye, Edit, Calendar, Zap } from 'lucide-react';
|
||||
import { Button, Input, Card, Badge, StatsGrid } from '../../../../components/ui';
|
||||
import { pagePresets } from '../../../../components/ui/Stats/StatsPresets';
|
||||
import { PageHeader } from '../../../../components/layout';
|
||||
import { ProductionSchedule, BatchTracker, QualityControl } from '../../../../components/domain/production';
|
||||
|
||||
const ProductionPage: React.FC = () => {
|
||||
const [activeTab, setActiveTab] = useState('schedule');
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [filters, setFilters] = useState<DataTableFilter[]>([]);
|
||||
const [selectedBatches, setSelectedBatches] = useState<any[]>([]);
|
||||
const [pagination, setPagination] = useState<DataTablePagination>({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
total: 8 // Updated to match the number of mock orders
|
||||
});
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [selectedOrder, setSelectedOrder] = useState<typeof mockProductionOrders[0] | null>(null);
|
||||
const [showForm, setShowForm] = useState(false);
|
||||
|
||||
const mockProductionStats = {
|
||||
dailyTarget: 150,
|
||||
@@ -27,41 +20,6 @@ const ProductionPage: React.FC = () => {
|
||||
quality: 94,
|
||||
};
|
||||
|
||||
// Handler functions for table actions
|
||||
const handleViewBatch = (batch: any) => {
|
||||
console.log('Ver lote:', batch);
|
||||
// Implement view logic
|
||||
};
|
||||
|
||||
const handleEditBatch = (batch: any) => {
|
||||
console.log('Editar lote:', batch);
|
||||
// Implement edit logic
|
||||
};
|
||||
|
||||
const handleSearchChange = (query: string) => {
|
||||
setSearchQuery(query);
|
||||
// Implement search logic
|
||||
};
|
||||
|
||||
const handleFiltersChange = (newFilters: DataTableFilter[]) => {
|
||||
setFilters(newFilters);
|
||||
// Implement filtering logic
|
||||
};
|
||||
|
||||
const handlePageChange = (page: number, pageSize: number) => {
|
||||
setPagination(prev => ({ ...prev, page, pageSize }));
|
||||
// Implement pagination logic
|
||||
};
|
||||
|
||||
const handleBatchSelection = (selectedRows: any[]) => {
|
||||
setSelectedBatches(selectedRows);
|
||||
};
|
||||
|
||||
const handleExport = (format: 'csv' | 'xlsx') => {
|
||||
console.log(`Exportando en formato ${format}`);
|
||||
// Implement export logic
|
||||
};
|
||||
|
||||
const mockProductionOrders = [
|
||||
{
|
||||
id: '1',
|
||||
@@ -155,248 +113,78 @@ const ProductionPage: React.FC = () => {
|
||||
|
||||
const getStatusBadge = (status: string) => {
|
||||
const statusConfig = {
|
||||
pending: { color: 'yellow', text: 'Pendiente' },
|
||||
in_progress: { color: 'blue', text: 'En Proceso' },
|
||||
completed: { color: 'green', text: 'Completado' },
|
||||
cancelled: { color: 'red', text: 'Cancelado' },
|
||||
pending: { color: 'warning', text: 'Pendiente', icon: Clock },
|
||||
in_progress: { color: 'info', text: 'En Proceso', icon: Timer },
|
||||
completed: { color: 'success', text: 'Completado', icon: CheckCircle },
|
||||
cancelled: { color: 'error', text: 'Cancelado', icon: AlertCircle },
|
||||
};
|
||||
|
||||
const config = statusConfig[status as keyof typeof statusConfig];
|
||||
return <Badge variant={config.color as any}>{config.text}</Badge>;
|
||||
const Icon = config?.icon;
|
||||
return (
|
||||
<Badge
|
||||
variant={config?.color as any}
|
||||
icon={Icon && <Icon size={12} />}
|
||||
text={config?.text || status}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const getPriorityBadge = (priority: string) => {
|
||||
const priorityConfig = {
|
||||
low: { color: 'gray', text: 'Baja' },
|
||||
medium: { color: 'yellow', text: 'Media' },
|
||||
high: { color: 'orange', text: 'Alta' },
|
||||
urgent: { color: 'red', text: 'Urgente' },
|
||||
low: { color: 'outline', text: 'Baja' },
|
||||
medium: { color: 'secondary', text: 'Media' },
|
||||
high: { color: 'warning', text: 'Alta' },
|
||||
urgent: { color: 'error', text: 'Urgente', icon: Zap },
|
||||
};
|
||||
|
||||
const config = priorityConfig[priority as keyof typeof priorityConfig];
|
||||
return <Badge variant={config.color as any}>{config.text}</Badge>;
|
||||
const Icon = config?.icon;
|
||||
return (
|
||||
<Badge
|
||||
variant={config?.color as any}
|
||||
icon={Icon && <Icon size={12} />}
|
||||
text={config?.text || priority}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const columns: DataTableColumn[] = [
|
||||
{
|
||||
id: 'recipeName',
|
||||
key: 'recipeName',
|
||||
header: 'Receta',
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
type: 'text',
|
||||
width: 200,
|
||||
cell: (value) => (
|
||||
<div className="text-sm font-medium text-[var(--text-primary)]">{value}</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'quantity',
|
||||
key: 'quantity',
|
||||
header: 'Cantidad',
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
type: 'number',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
cell: (value) => `${value} unidades`,
|
||||
},
|
||||
{
|
||||
id: 'status',
|
||||
key: 'status',
|
||||
header: 'Estado',
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
type: 'select',
|
||||
width: 130,
|
||||
align: 'center',
|
||||
selectOptions: [
|
||||
{ value: 'pending', label: 'Pendiente' },
|
||||
{ value: 'in_progress', label: 'En Proceso' },
|
||||
{ value: 'completed', label: 'Completado' },
|
||||
{ value: 'cancelled', label: 'Cancelado' }
|
||||
],
|
||||
cell: (value) => getStatusBadge(value),
|
||||
},
|
||||
{
|
||||
id: 'priority',
|
||||
key: 'priority',
|
||||
header: 'Prioridad',
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
type: 'select',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
selectOptions: [
|
||||
{ value: 'low', label: 'Baja' },
|
||||
{ value: 'medium', label: 'Media' },
|
||||
{ value: 'high', label: 'Alta' },
|
||||
{ value: 'urgent', label: 'Urgente' }
|
||||
],
|
||||
cell: (value) => getPriorityBadge(value),
|
||||
},
|
||||
{
|
||||
id: 'assignedTo',
|
||||
key: 'assignedTo',
|
||||
header: 'Asignado a',
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
type: 'text',
|
||||
width: 180,
|
||||
hideOnMobile: true,
|
||||
cell: (value) => (
|
||||
<div className="flex items-center">
|
||||
<Users className="h-4 w-4 text-[var(--text-tertiary)] mr-2" />
|
||||
<span className="text-sm text-[var(--text-primary)]">{value}</span>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'progress',
|
||||
key: 'progress',
|
||||
header: 'Progreso',
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
type: 'number',
|
||||
width: 150,
|
||||
align: 'center',
|
||||
hideOnMobile: true,
|
||||
cell: (value) => (
|
||||
<div className="flex items-center">
|
||||
<div className="w-full bg-[var(--bg-quaternary)] rounded-full h-2 mr-2">
|
||||
<div
|
||||
className="bg-blue-600 h-2 rounded-full"
|
||||
style={{ width: `${value}%` }}
|
||||
></div>
|
||||
</div>
|
||||
<span className="text-sm text-[var(--text-primary)]">{value}%</span>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'estimatedCompletion',
|
||||
key: 'estimatedCompletion',
|
||||
header: 'Tiempo Estimado',
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
type: 'date',
|
||||
width: 140,
|
||||
align: 'center',
|
||||
hideOnMobile: true,
|
||||
cell: (value) => new Date(value).toLocaleTimeString('es-ES', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: 'actions',
|
||||
key: 'actions',
|
||||
header: 'Acciones',
|
||||
sortable: false,
|
||||
filterable: false,
|
||||
width: 150,
|
||||
align: 'right',
|
||||
sticky: 'right',
|
||||
cell: (value, row) => (
|
||||
<div className="flex space-x-2">
|
||||
<Button variant="outline" size="sm" onClick={() => handleViewBatch(row)}>
|
||||
Ver
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" onClick={() => handleEditBatch(row)}>
|
||||
Editar
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
const filteredOrders = mockProductionOrders.filter(order => {
|
||||
const matchesSearch = order.recipeName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
order.assignedTo.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
order.id.toLowerCase().includes(searchQuery.toLowerCase());
|
||||
|
||||
return matchesSearch;
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="p-6 space-y-6">
|
||||
<div className="space-y-6">
|
||||
<PageHeader
|
||||
title="Gestión de Producción"
|
||||
description="Planifica y controla la producción diaria de tu panadería"
|
||||
action={
|
||||
<Button>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
Nueva Orden de Producción
|
||||
</Button>
|
||||
}
|
||||
actions={[
|
||||
{
|
||||
id: "export",
|
||||
label: "Exportar",
|
||||
variant: "outline" as const,
|
||||
icon: Download,
|
||||
onClick: () => console.log('Export production orders')
|
||||
},
|
||||
{
|
||||
id: "new",
|
||||
label: "Nueva Orden de Producción",
|
||||
variant: "primary" as const,
|
||||
icon: Plus,
|
||||
onClick: () => setShowForm(true)
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* Production Stats */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-6 gap-4">
|
||||
<Card className="p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-[var(--text-secondary)]">Meta Diaria</p>
|
||||
<p className="text-2xl font-bold text-[var(--text-primary)]">{mockProductionStats.dailyTarget}</p>
|
||||
</div>
|
||||
<Calendar className="h-8 w-8 text-[var(--color-info)]" />
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card className="p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-[var(--text-secondary)]">Completado</p>
|
||||
<p className="text-2xl font-bold text-[var(--color-success)]">{mockProductionStats.completed}</p>
|
||||
</div>
|
||||
<div className="h-8 w-8 bg-[var(--color-success)]/10 rounded-full flex items-center justify-center">
|
||||
<svg className="h-5 w-5 text-[var(--color-success)]" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card className="p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-[var(--text-secondary)]">En Proceso</p>
|
||||
<p className="text-2xl font-bold text-[var(--color-info)]">{mockProductionStats.inProgress}</p>
|
||||
</div>
|
||||
<Clock className="h-8 w-8 text-[var(--color-info)]" />
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card className="p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-[var(--text-secondary)]">Pendiente</p>
|
||||
<p className="text-2xl font-bold text-[var(--color-primary)]">{mockProductionStats.pending}</p>
|
||||
</div>
|
||||
<AlertCircle className="h-8 w-8 text-[var(--color-primary)]" />
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card className="p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-[var(--text-secondary)]">Eficiencia</p>
|
||||
<p className="text-2xl font-bold text-purple-600">{mockProductionStats.efficiency}%</p>
|
||||
</div>
|
||||
<div className="h-8 w-8 bg-purple-100 rounded-full flex items-center justify-center">
|
||||
<svg className="h-5 w-5 text-purple-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card className="p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-[var(--text-secondary)]">Calidad</p>
|
||||
<p className="text-2xl font-bold text-indigo-600">{mockProductionStats.quality}%</p>
|
||||
</div>
|
||||
<div className="h-8 w-8 bg-indigo-100 rounded-full flex items-center justify-center">
|
||||
<svg className="h-5 w-5 text-indigo-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
<StatsGrid
|
||||
stats={pagePresets.production(mockProductionStats)}
|
||||
columns={6}
|
||||
/>
|
||||
|
||||
{/* Tabs Navigation */}
|
||||
<div className="border-b border-[var(--border-primary)]">
|
||||
@@ -434,56 +222,162 @@ const ProductionPage: React.FC = () => {
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
{/* Tab Content */}
|
||||
{/* Production Orders Tab */}
|
||||
{activeTab === 'schedule' && (
|
||||
<Card>
|
||||
<div className="p-6">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h3 className="text-lg font-medium text-[var(--text-primary)]">Órdenes de Producción</h3>
|
||||
<div className="flex space-x-2">
|
||||
{selectedBatches.length > 0 && (
|
||||
<Button variant="outline" size="sm">
|
||||
<Download className="w-4 h-4 mr-2" />
|
||||
Acciones en lote ({selectedBatches.length})
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="outline" size="sm">
|
||||
<Filter className="w-4 h-4 mr-2" />
|
||||
Vista Calendario
|
||||
</Button>
|
||||
<>
|
||||
{/* Simplified Controls */}
|
||||
<Card className="p-4">
|
||||
<div className="flex flex-col sm:flex-row gap-4">
|
||||
<div className="flex-1">
|
||||
<Input
|
||||
placeholder="Buscar órdenes por receta, asignado o ID..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
<Button variant="outline" onClick={() => console.log('Export filtered')}>
|
||||
<Download className="w-4 h-4 mr-2" />
|
||||
Exportar
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<DataTable
|
||||
data={mockProductionOrders}
|
||||
columns={columns}
|
||||
isLoading={isLoading}
|
||||
searchQuery={searchQuery}
|
||||
onSearchChange={handleSearchChange}
|
||||
searchPlaceholder="Buscar por receta, asignado, estado..."
|
||||
filters={filters}
|
||||
onFiltersChange={handleFiltersChange}
|
||||
pagination={pagination}
|
||||
onPageChange={handlePageChange}
|
||||
selection={{
|
||||
mode: 'multiple',
|
||||
selectedRows: selectedBatches,
|
||||
onSelectionChange: handleBatchSelection,
|
||||
getRowId: (row) => row.id
|
||||
}}
|
||||
enableExport={true}
|
||||
onExport={handleExport}
|
||||
density="normal"
|
||||
horizontalScroll={true}
|
||||
emptyStateMessage="No se encontraron órdenes de producción"
|
||||
emptyStateAction={{
|
||||
label: "Nueva Orden",
|
||||
onClick: () => console.log('Nueva orden de producción')
|
||||
}}
|
||||
onRowClick={(row) => console.log('Ver detalles:', row)}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
{/* Production Orders Grid */}
|
||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
{filteredOrders.map((order) => (
|
||||
<Card key={order.id} className="p-4">
|
||||
<div className="space-y-4">
|
||||
{/* Header */}
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="flex-shrink-0 bg-[var(--color-primary)]/10 p-2 rounded-lg">
|
||||
<ChefHat className="w-4 h-4 text-[var(--text-tertiary)]" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-medium text-[var(--text-primary)]">
|
||||
{order.recipeName}
|
||||
</div>
|
||||
<div className="text-sm text-[var(--text-secondary)]">
|
||||
ID: {order.id}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{getStatusBadge(order.status)}
|
||||
</div>
|
||||
|
||||
{/* Priority and Quantity */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
{getPriorityBadge(order.priority)}
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<div className="text-lg font-bold text-[var(--text-primary)]">
|
||||
{order.quantity}
|
||||
</div>
|
||||
<div className="text-xs text-[var(--text-tertiary)]">
|
||||
unidades
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Assigned Worker */}
|
||||
<div className="flex items-center gap-2">
|
||||
<Users className="w-4 h-4 text-[var(--text-tertiary)]" />
|
||||
<span className="text-sm text-[var(--text-primary)]">
|
||||
{order.assignedTo}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Time Information */}
|
||||
<div className="space-y-2 text-xs">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-[var(--text-secondary)]">Inicio:</span>
|
||||
<span className="text-[var(--text-primary)]">
|
||||
{new Date(order.startTime).toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit' })}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-[var(--text-secondary)]">Est. finalización:</span>
|
||||
<span className="text-[var(--text-primary)]">
|
||||
{new Date(order.estimatedCompletion).toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit' })}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Progress Bar */}
|
||||
<div className="space-y-1">
|
||||
<div className="flex justify-between text-xs">
|
||||
<span className="text-[var(--text-secondary)]">Progreso</span>
|
||||
<span className="text-[var(--text-primary)]">
|
||||
{order.progress}%
|
||||
</span>
|
||||
</div>
|
||||
<div className="w-full bg-[var(--bg-tertiary)] rounded-full h-2">
|
||||
<div
|
||||
className={`h-2 rounded-full transition-all duration-300 ${
|
||||
order.progress === 100
|
||||
? 'bg-[var(--color-success)]'
|
||||
: order.progress > 50
|
||||
? 'bg-[var(--color-info)]'
|
||||
: order.progress > 0
|
||||
? 'bg-[var(--color-warning)]'
|
||||
: 'bg-[var(--bg-quaternary)]'
|
||||
}`}
|
||||
style={{ width: `${order.progress}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex gap-2 pt-2 border-t border-[var(--border-primary)]">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="flex-1"
|
||||
onClick={() => {
|
||||
setSelectedOrder(order);
|
||||
setShowForm(true);
|
||||
}}
|
||||
>
|
||||
<Eye className="w-4 h-4 mr-1" />
|
||||
Ver
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="flex-1"
|
||||
onClick={() => {
|
||||
setSelectedOrder(order);
|
||||
setShowForm(true);
|
||||
}}
|
||||
>
|
||||
<Edit className="w-4 h-4 mr-1" />
|
||||
Editar
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Empty State */}
|
||||
{filteredOrders.length === 0 && (
|
||||
<div className="text-center py-12">
|
||||
<ChefHat className="mx-auto h-12 w-12 text-[var(--text-tertiary)] mb-4" />
|
||||
<h3 className="text-lg font-medium text-[var(--text-primary)] mb-2">
|
||||
No se encontraron órdenes de producción
|
||||
</h3>
|
||||
<p className="text-[var(--text-secondary)] mb-4">
|
||||
Intenta ajustar la búsqueda o crear una nueva orden de producción
|
||||
</p>
|
||||
<Button onClick={() => setShowForm(true)}>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
Nueva Orden de Producción
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{activeTab === 'batches' && (
|
||||
@@ -493,6 +387,54 @@ const ProductionPage: React.FC = () => {
|
||||
{activeTab === 'quality' && (
|
||||
<QualityControl />
|
||||
)}
|
||||
|
||||
{/* Production Order Form Modal - Placeholder */}
|
||||
{showForm && (
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
|
||||
<Card className="w-full max-w-2xl max-h-[90vh] overflow-auto m-4 p-6">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h2 className="text-xl font-semibold text-[var(--text-primary)]">
|
||||
{selectedOrder ? 'Ver Orden de Producción' : 'Nueva Orden de Producción'}
|
||||
</h2>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
setShowForm(false);
|
||||
setSelectedOrder(null);
|
||||
}}
|
||||
>
|
||||
Cerrar
|
||||
</Button>
|
||||
</div>
|
||||
{selectedOrder && (
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-lg font-medium">{selectedOrder.recipeName}</h3>
|
||||
<div className="grid grid-cols-2 gap-4 text-sm">
|
||||
<div>
|
||||
<span className="font-medium">Cantidad:</span> {selectedOrder.quantity} unidades
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-medium">Asignado a:</span> {selectedOrder.assignedTo}
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-medium">Estado:</span> {selectedOrder.status}
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-medium">Progreso:</span> {selectedOrder.progress}%
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-medium">Inicio:</span> {new Date(selectedOrder.startTime).toLocaleString('es-ES')}
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-medium">Finalización:</span> {new Date(selectedOrder.estimatedCompletion).toLocaleString('es-ES')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user