Improve the frontend and fix TODOs

This commit is contained in:
Urtzi Alfaro
2025-10-24 13:05:04 +02:00
parent 07c33fa578
commit 61376b7a9f
100 changed files with 8284 additions and 3419 deletions

View File

@@ -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 {

View File

@@ -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

View File

@@ -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;