Improve the frontend and fix TODOs
This commit is contained in:
@@ -61,12 +61,13 @@ const InventoryPage: React.FC = () => {
|
||||
isLoading: analyticsLoading
|
||||
} = useStockAnalytics(tenantId);
|
||||
|
||||
// TODO: Implement expired stock API endpoint
|
||||
// Expiring stock data (already implemented via useExpiringStock hook)
|
||||
// Uncomment below if you need to display expired stock separately:
|
||||
// const {
|
||||
// data: expiredStockData,
|
||||
// isLoading: expiredStockLoading,
|
||||
// error: expiredStockError
|
||||
// } = useExpiredStock(tenantId);
|
||||
// data: expiringStockData,
|
||||
// isLoading: expiringStockLoading,
|
||||
// error: expiringStockError
|
||||
// } = useExpiringStock(tenantId, 7); // items expiring within 7 days
|
||||
|
||||
// Stock movements for history modal
|
||||
const {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import { Plus, Clock, AlertCircle, CheckCircle, Timer, ChefHat, Eye, Edit, Package, PlusCircle } from 'lucide-react';
|
||||
import { Plus, Clock, AlertCircle, CheckCircle, Timer, ChefHat, Eye, Edit, Package, PlusCircle, Play } from 'lucide-react';
|
||||
import { Button, StatsGrid, EditViewModal, Toggle, SearchAndFilter, type FilterConfig } from '../../../../components/ui';
|
||||
import { statusColors } from '../../../../styles/colors';
|
||||
import { formatters } from '../../../../components/ui/Stats/StatsPresets';
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
useActiveBatches,
|
||||
useCreateProductionBatch,
|
||||
useUpdateBatchStatus,
|
||||
useTriggerProductionScheduler,
|
||||
productionService
|
||||
} from '../../../../api';
|
||||
import type {
|
||||
@@ -25,6 +26,7 @@ import {
|
||||
} from '../../../../api';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ProcessStage } from '../../../../api/types/qualityTemplates';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
const ProductionPage: React.FC = () => {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
@@ -56,6 +58,7 @@ const ProductionPage: React.FC = () => {
|
||||
// Mutations
|
||||
const createBatchMutation = useCreateProductionBatch();
|
||||
const updateBatchStatusMutation = useUpdateBatchStatus();
|
||||
const triggerSchedulerMutation = useTriggerProductionScheduler();
|
||||
|
||||
// Handlers
|
||||
const handleCreateBatch = async (batchData: ProductionBatchCreate) => {
|
||||
@@ -70,6 +73,16 @@ const ProductionPage: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleTriggerScheduler = async () => {
|
||||
try {
|
||||
await triggerSchedulerMutation.mutateAsync(tenantId);
|
||||
toast.success('Scheduler ejecutado exitosamente');
|
||||
} catch (error) {
|
||||
console.error('Error triggering scheduler:', error);
|
||||
toast.error('Error al ejecutar scheduler');
|
||||
}
|
||||
};
|
||||
|
||||
// Stage management handlers
|
||||
const handleStageAdvance = async (batchId: string, currentStage: ProcessStage) => {
|
||||
const stages = Object.values(ProcessStage);
|
||||
@@ -283,21 +296,30 @@ const ProductionPage: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<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"
|
||||
/>
|
||||
<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>
|
||||
<PageHeader
|
||||
title="Gestión de Producción"
|
||||
description="Planifica y controla la producción diaria de tu panadería"
|
||||
actions={[
|
||||
{
|
||||
id: 'trigger-scheduler',
|
||||
label: triggerSchedulerMutation.isPending ? 'Ejecutando...' : 'Ejecutar Scheduler',
|
||||
icon: Play,
|
||||
onClick: handleTriggerScheduler,
|
||||
variant: 'outline',
|
||||
size: 'sm',
|
||||
disabled: triggerSchedulerMutation.isPending,
|
||||
loading: triggerSchedulerMutation.isPending
|
||||
},
|
||||
{
|
||||
id: 'create-batch',
|
||||
label: 'Nueva Orden de Producción',
|
||||
icon: PlusCircle,
|
||||
onClick: () => setShowCreateModal(true),
|
||||
variant: 'primary',
|
||||
size: 'md'
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* Production Stats */}
|
||||
<StatsGrid
|
||||
|
||||
@@ -317,48 +317,48 @@ const SuppliersPage: React.FC = () => {
|
||||
icon: Users,
|
||||
fields: [
|
||||
{
|
||||
label: 'Nombre',
|
||||
label: t('common:fields.name'),
|
||||
value: selectedSupplier.name || '',
|
||||
type: 'text',
|
||||
type: 'text' as const,
|
||||
highlight: true,
|
||||
editable: true,
|
||||
required: true,
|
||||
placeholder: 'Nombre del proveedor'
|
||||
placeholder: t('suppliers:placeholders.name')
|
||||
},
|
||||
{
|
||||
label: 'Persona de Contacto',
|
||||
label: t('common:fields.contact_person'),
|
||||
value: selectedSupplier.contact_person || '',
|
||||
type: 'text',
|
||||
type: 'text' as const,
|
||||
editable: true,
|
||||
placeholder: 'Nombre del contacto'
|
||||
placeholder: t('suppliers:placeholders.contact_person')
|
||||
},
|
||||
{
|
||||
label: 'Email',
|
||||
label: t('common:fields.email'),
|
||||
value: selectedSupplier.email || '',
|
||||
type: 'email',
|
||||
type: 'email' as const,
|
||||
editable: true,
|
||||
placeholder: 'email@ejemplo.com'
|
||||
placeholder: t('common:fields.email_placeholder')
|
||||
},
|
||||
{
|
||||
label: 'Teléfono',
|
||||
label: t('common:fields.phone'),
|
||||
value: selectedSupplier.phone || '',
|
||||
type: 'tel',
|
||||
type: 'tel' as const,
|
||||
editable: true,
|
||||
placeholder: '+34 123 456 789'
|
||||
placeholder: t('common:fields.phone_placeholder')
|
||||
},
|
||||
{
|
||||
label: 'Ciudad',
|
||||
label: t('common:fields.city'),
|
||||
value: selectedSupplier.city || '',
|
||||
type: 'text',
|
||||
type: 'text' as const,
|
||||
editable: true,
|
||||
placeholder: 'Ciudad'
|
||||
placeholder: t('common:fields.city')
|
||||
},
|
||||
{
|
||||
label: 'País',
|
||||
label: t('common:fields.country'),
|
||||
value: selectedSupplier.country || '',
|
||||
type: 'text',
|
||||
type: 'text' as const,
|
||||
editable: true,
|
||||
placeholder: 'País'
|
||||
placeholder: t('common:fields.country')
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -367,90 +367,94 @@ const SuppliersPage: React.FC = () => {
|
||||
icon: Building2,
|
||||
fields: [
|
||||
{
|
||||
label: 'Código de Proveedor',
|
||||
label: t('suppliers:labels.supplier_code'),
|
||||
value: selectedSupplier.supplier_code || '',
|
||||
type: 'text',
|
||||
type: 'text' as const,
|
||||
highlight: true,
|
||||
editable: true,
|
||||
placeholder: 'Código único'
|
||||
placeholder: t('suppliers:placeholders.supplier_code')
|
||||
},
|
||||
{
|
||||
label: 'Tipo de Proveedor',
|
||||
value: selectedSupplier.supplier_type || SupplierType.INGREDIENTS,
|
||||
type: 'select',
|
||||
label: t('suppliers:labels.supplier_type'),
|
||||
value: modalMode === 'view'
|
||||
? getSupplierTypeText(selectedSupplier.supplier_type || SupplierType.INGREDIENTS)
|
||||
: selectedSupplier.supplier_type || SupplierType.INGREDIENTS,
|
||||
type: modalMode === 'view' ? 'text' as const : 'select' as const,
|
||||
editable: true,
|
||||
options: Object.values(SupplierType).map(value => ({
|
||||
options: modalMode === 'edit' ? Object.values(SupplierType).map(value => ({
|
||||
value,
|
||||
label: t(`suppliers:types.${value.toLowerCase()}`)
|
||||
}))
|
||||
})) : undefined
|
||||
},
|
||||
{
|
||||
label: 'Condiciones de Pago',
|
||||
value: selectedSupplier.payment_terms || PaymentTerms.NET_30,
|
||||
type: 'select',
|
||||
label: t('suppliers:labels.payment_terms'),
|
||||
value: modalMode === 'view'
|
||||
? getPaymentTermsText(selectedSupplier.payment_terms || PaymentTerms.NET_30)
|
||||
: selectedSupplier.payment_terms || PaymentTerms.NET_30,
|
||||
type: modalMode === 'view' ? 'text' as const : 'select' as const,
|
||||
editable: true,
|
||||
options: Object.values(PaymentTerms).map(value => ({
|
||||
options: modalMode === 'edit' ? Object.values(PaymentTerms).map(value => ({
|
||||
value,
|
||||
label: t(`suppliers:payment_terms.${value.toLowerCase()}`)
|
||||
}))
|
||||
})) : undefined
|
||||
},
|
||||
{
|
||||
label: 'Tiempo de Entrega (días)',
|
||||
label: t('suppliers:labels.lead_time'),
|
||||
value: selectedSupplier.standard_lead_time || 3,
|
||||
type: 'number',
|
||||
type: 'number' as const,
|
||||
editable: true,
|
||||
placeholder: '3'
|
||||
},
|
||||
{
|
||||
label: 'Pedido Mínimo',
|
||||
label: t('suppliers:labels.minimum_order'),
|
||||
value: selectedSupplier.minimum_order_amount || 0,
|
||||
type: 'currency',
|
||||
type: 'currency' as const,
|
||||
editable: true,
|
||||
placeholder: '0.00'
|
||||
},
|
||||
{
|
||||
label: 'Límite de Crédito',
|
||||
label: t('suppliers:labels.credit_limit'),
|
||||
value: selectedSupplier.credit_limit || 0,
|
||||
type: 'currency',
|
||||
type: 'currency' as const,
|
||||
editable: true,
|
||||
placeholder: '0.00'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Rendimiento y Estadísticas',
|
||||
title: t('suppliers:sections.performance'),
|
||||
icon: Euro,
|
||||
fields: [
|
||||
{
|
||||
label: 'Moneda',
|
||||
label: t('suppliers:labels.currency'),
|
||||
value: selectedSupplier.currency || 'EUR',
|
||||
type: 'text',
|
||||
type: 'text' as const,
|
||||
editable: true,
|
||||
placeholder: 'EUR'
|
||||
},
|
||||
{
|
||||
label: 'Fecha de Creación',
|
||||
label: t('suppliers:labels.created_date'),
|
||||
value: selectedSupplier.created_at,
|
||||
type: 'datetime',
|
||||
type: 'datetime' as const,
|
||||
highlight: true
|
||||
},
|
||||
{
|
||||
label: 'Última Actualización',
|
||||
label: t('suppliers:labels.updated_date'),
|
||||
value: selectedSupplier.updated_at,
|
||||
type: 'datetime'
|
||||
type: 'datetime' as const
|
||||
}
|
||||
]
|
||||
},
|
||||
...(selectedSupplier.notes ? [{
|
||||
title: 'Notas',
|
||||
title: t('suppliers:sections.notes'),
|
||||
fields: [
|
||||
{
|
||||
label: 'Observaciones',
|
||||
label: t('suppliers:labels.notes'),
|
||||
value: selectedSupplier.notes,
|
||||
type: 'list',
|
||||
type: 'list' as const,
|
||||
span: 2 as const,
|
||||
editable: true,
|
||||
placeholder: 'Notas sobre el proveedor'
|
||||
placeholder: t('suppliers:placeholders.notes')
|
||||
}
|
||||
]
|
||||
}] : [])
|
||||
@@ -472,7 +476,7 @@ const SuppliersPage: React.FC = () => {
|
||||
statusIndicator={isCreating ? undefined : getSupplierStatusConfig(selectedSupplier.status)}
|
||||
size="lg"
|
||||
sections={sections}
|
||||
showDefaultActions={true}
|
||||
showDefaultActions={modalMode === 'edit'}
|
||||
onSave={async () => {
|
||||
// TODO: Implement save functionality
|
||||
console.log('Saving supplier:', selectedSupplier);
|
||||
@@ -485,20 +489,20 @@ const SuppliersPage: React.FC = () => {
|
||||
|
||||
// Map field labels to supplier properties
|
||||
const fieldMapping: { [key: string]: string } = {
|
||||
'Nombre': 'name',
|
||||
'Persona de Contacto': 'contact_person',
|
||||
'Email': 'email',
|
||||
'Teléfono': 'phone',
|
||||
'Ciudad': 'city',
|
||||
'País': 'country',
|
||||
'Código de Proveedor': 'supplier_code',
|
||||
'Tipo de Proveedor': 'supplier_type',
|
||||
'Condiciones de Pago': 'payment_terms',
|
||||
'Tiempo de Entrega (días)': 'standard_lead_time',
|
||||
'Pedido Mínimo': 'minimum_order_amount',
|
||||
'Límite de Crédito': 'credit_limit',
|
||||
'Moneda': 'currency',
|
||||
'Observaciones': 'notes'
|
||||
[t('common:fields.name')]: 'name',
|
||||
[t('common:fields.contact_person')]: 'contact_person',
|
||||
[t('common:fields.email')]: 'email',
|
||||
[t('common:fields.phone')]: 'phone',
|
||||
[t('common:fields.city')]: 'city',
|
||||
[t('common:fields.country')]: 'country',
|
||||
[t('suppliers:labels.supplier_code')]: 'supplier_code',
|
||||
[t('suppliers:labels.supplier_type')]: 'supplier_type',
|
||||
[t('suppliers:labels.payment_terms')]: 'payment_terms',
|
||||
[t('suppliers:labels.lead_time')]: 'standard_lead_time',
|
||||
[t('suppliers:labels.minimum_order')]: 'minimum_order_amount',
|
||||
[t('suppliers:labels.credit_limit')]: 'credit_limit',
|
||||
[t('suppliers:labels.currency')]: 'currency',
|
||||
[t('suppliers:labels.notes')]: 'notes'
|
||||
};
|
||||
|
||||
const propertyName = fieldMapping[field.label];
|
||||
@@ -514,4 +518,4 @@ const SuppliersPage: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default SuppliersPage;
|
||||
export default SuppliersPage;
|
||||
|
||||
Reference in New Issue
Block a user