Imporve the i18 and frontend UI pages

This commit is contained in:
Urtzi Alfaro
2025-09-22 16:10:08 +02:00
parent ee36c45d25
commit 8d54202e91
32 changed files with 875 additions and 434 deletions

View File

@@ -544,4 +544,5 @@ export const useTriggerDailyScheduler = (
},
...options,
});
};
};

View File

@@ -302,6 +302,7 @@ export class OrdersService {
static async getProcurementHealth(tenantId: string): Promise<{ status: string; service: string; procurement_enabled: boolean; timestamp: string }> {
return apiClient.get<{ status: string; service: string; procurement_enabled: boolean; timestamp: string }>(`/tenants/${tenantId}/procurement/health`);
}
}
export default OrdersService;

View File

@@ -12,7 +12,7 @@ import {
ChevronRight,
Calendar,
User,
DollarSign,
Euro,
Truck
} from 'lucide-react';

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect, useMemo } from 'react';
import { ChefHat, Package, Clock, DollarSign, Star } from 'lucide-react';
import { ChefHat, Package, Clock, Euro, Star } from 'lucide-react';
import { StatusModal } from '../../ui/StatusModal/StatusModal';
import { RecipeCreate, RecipeIngredientCreate, MeasurementUnit } from '../../../api/types/recipes';
import { useIngredients } from '../../../api/hooks/inventory';
@@ -402,7 +402,7 @@ export const CreateRecipeModal: React.FC<CreateRecipeModalProps> = ({
},
{
title: 'Configuración Financiera',
icon: DollarSign,
icon: Euro,
fields: [
{
key: 'estimated_cost_per_unit',

View File

@@ -8,7 +8,7 @@ import {
TrendingUp,
Package,
Users,
DollarSign,
Euro,
BarChart3,
Target,
Activity,
@@ -40,7 +40,7 @@ export const statIcons = {
growth: TrendingUp,
inventory: Package,
users: Users,
revenue: DollarSign,
revenue: Euro,
analytics: BarChart3,
goals: Target,
activity: Activity,

View File

@@ -1,4 +1,4 @@
import { useCallback } from 'react';
import { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useUIStore } from '../stores/ui.store';
import { type SupportedLanguage } from '../locales';
@@ -10,18 +10,20 @@ import { type SupportedLanguage } from '../locales';
export function useLanguageSwitcher() {
const { i18n } = useTranslation();
const { language: uiLanguage, setLanguage: setUILanguage } = useUIStore();
const [isChanging, setIsChanging] = useState(false);
const changeLanguage = useCallback(async (newLanguage: SupportedLanguage) => {
try {
setIsChanging(true);
// Only change i18n language - let the i18n event handler update UI store
await i18n.changeLanguage(newLanguage);
setIsChanging(false);
} catch (error) {
console.error('Failed to change language:', error);
setIsChanging(false);
}
}, [i18n]);
const isChanging = i18n.isLanguageChangingTo !== false;
return {
currentLanguage: i18n.language as SupportedLanguage,
changeLanguage,

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -12,12 +12,25 @@ import productionEs from './es/production.json';
// English translations
import commonEn from './en/common.json';
import authEn from './en/auth.json';
import inventoryEn from './en/inventory.json';
import foodSafetyEn from './en/foodSafety.json';
import suppliersEn from './en/suppliers.json';
import ordersEn from './en/orders.json';
import recipesEn from './en/recipes.json';
import errorsEn from './en/errors.json';
import dashboardEn from './en/dashboard.json';
import productionEn from './en/production.json';
// Basque translations
import commonEu from './eu/common.json';
import authEu from './eu/auth.json';
import inventoryEu from './eu/inventory.json';
import foodSafetyEu from './eu/foodSafety.json';
import suppliersEu from './eu/suppliers.json';
import ordersEu from './eu/orders.json';
import recipesEu from './eu/recipes.json';
import errorsEu from './eu/errors.json';
import dashboardEu from './eu/dashboard.json';
import productionEu from './eu/production.json';
@@ -37,12 +50,25 @@ export const resources = {
},
en: {
common: commonEn,
auth: authEn,
inventory: inventoryEn,
foodSafety: foodSafetyEn,
suppliers: suppliersEn,
orders: ordersEn,
recipes: recipesEn,
errors: errorsEn,
dashboard: dashboardEn,
production: productionEn,
},
eu: {
common: commonEu,
auth: authEu,
inventory: inventoryEu,
foodSafety: foodSafetyEu,
suppliers: suppliersEu,
orders: ordersEu,
recipes: recipesEu,
errors: errorsEu,
dashboard: dashboardEu,
production: productionEu,
},

View File

@@ -12,7 +12,7 @@ import { useTenant } from '../../stores/tenant.store';
import {
AlertTriangle,
Clock,
DollarSign,
Euro,
Package,
TrendingUp,
TrendingDown,
@@ -33,7 +33,7 @@ const DashboardPage: React.FC = () => {
{
title: t('dashboard:stats.sales_today', 'Sales Today'),
value: '€1,247',
icon: DollarSign,
icon: Euro,
variant: 'success' as const,
trend: {
value: 12,

View File

@@ -1,5 +1,5 @@
import React, { useState, useMemo } from 'react';
import { Calendar, TrendingUp, DollarSign, ShoppingCart, Download, Filter, Eye, Users, Package, CreditCard, BarChart3, AlertTriangle, Clock } from 'lucide-react';
import { Calendar, TrendingUp, Euro, ShoppingCart, Download, Filter, Eye, Users, Package, CreditCard, BarChart3, AlertTriangle, Clock } from 'lucide-react';
import { Button, Card, Badge, StatsGrid, StatusCard, getStatusColor } from '../../../../components/ui';
import { PageHeader } from '../../../../components/layout';
import { LoadingSpinner } from '../../../../components/shared';
@@ -430,7 +430,7 @@ const SalesAnalyticsPage: React.FC = () => {
title: 'Ingresos Totales',
value: formatters.currency(salesMetrics.totalRevenue),
variant: 'success' as const,
icon: DollarSign,
icon: Euro,
},
{
title: 'Total Transacciones',
@@ -711,7 +711,7 @@ const SalesAnalyticsPage: React.FC = () => {
</p>
</div>
<div className="flex items-center gap-1">
<DollarSign className="w-3 h-3 text-[var(--color-success)]" />
<Euro className="w-3 h-3 text-[var(--color-success)]" />
<span className="text-xs text-[var(--text-tertiary)]">#{index + 1}</span>
</div>
</div>

View File

@@ -1,5 +1,5 @@
import React, { useState } from 'react';
import { Plus, Download, ShoppingCart, Truck, DollarSign, Calendar, Clock, CheckCircle, AlertCircle, Package, Eye, Loader, Edit, ArrowRight, X, Save, Building2, Play } from 'lucide-react';
import { Plus, ShoppingCart, Truck, Euro, Calendar, Clock, CheckCircle, AlertCircle, Package, Eye, Loader, Edit, ArrowRight, X, Save, Building2, Play } from 'lucide-react';
import { Button, Input, Card, StatsGrid, StatusCard, getStatusColor, StatusModal } from '../../../../components/ui';
import { formatters } from '../../../../components/ui/Stats/StatsPresets';
import { PageHeader } from '../../../../components/layout';
@@ -22,6 +22,23 @@ const ProcurementPage: React.FC = () => {
const [editFormData, setEditFormData] = useState<any>({});
const [selectedPlanForRequirements, setSelectedPlanForRequirements] = useState<string | null>(null);
const [showCriticalRequirements, setShowCriticalRequirements] = useState(false);
const [showGeneratePlanModal, setShowGeneratePlanModal] = useState(false);
const [showRequirementDetailsModal, setShowRequirementDetailsModal] = useState(false);
const [selectedRequirement, setSelectedRequirement] = useState<any>(null);
const [generatePlanForm, setGeneratePlanForm] = useState({
plan_date: new Date().toISOString().split('T')[0],
planning_horizon_days: 14,
include_safety_stock: true,
safety_stock_percentage: 20,
force_regenerate: false
});
// Requirement details functionality
const handleViewRequirementDetails = (requirement: any) => {
setSelectedRequirement(requirement);
setShowRequirementDetailsModal(true);
};
const { currentTenant } = useTenantStore();
const tenantId = currentTenant?.id || '';
@@ -55,13 +72,6 @@ const ProcurementPage: React.FC = () => {
return isLowStock || isNearDeadline || hasHighPriority;
});
// Debug logging
console.log('📊 Plan Requirements Debug:', {
selectedPlanId: selectedPlanForRequirements,
allRequirements: allPlanRequirements?.length || 0,
criticalRequirements: planRequirements?.length || 0,
sampleRequirement: allPlanRequirements?.[0]
});
const generatePlanMutation = useGenerateProcurementPlan();
const updatePlanStatusMutation = useUpdateProcurementPlanStatus();
@@ -123,7 +133,6 @@ const ProcurementPage: React.FC = () => {
const handleSaveEdit = () => {
// For now, we'll just update the special requirements since that's the main editable field
// In a real implementation, you might have a separate API endpoint for updating plan details
console.log('Saving plan edits:', editFormData);
setEditingPlan(null);
setEditFormData({});
// Here you would typically call an update API
@@ -135,7 +144,6 @@ const ProcurementPage: React.FC = () => {
};
const handleShowCriticalRequirements = (planId: string) => {
console.log('🔍 Opening critical requirements for plan:', planId);
setSelectedPlanForRequirements(planId);
setShowCriticalRequirements(true);
};
@@ -191,6 +199,7 @@ const ProcurementPage: React.FC = () => {
return matchesSearch;
}) || [];
const stats = {
totalPlans: dashboardData?.summary?.total_plans || 0,
activePlans: dashboardData?.summary?.active_plans || 0,
@@ -229,13 +238,13 @@ const ProcurementPage: React.FC = () => {
title: 'Costo Estimado',
value: formatters.currency(stats.totalEstimatedCost),
variant: 'info' as const,
icon: DollarSign,
icon: Euro,
},
{
title: 'Costo Aprobado',
value: formatters.currency(stats.totalApprovedCost),
variant: 'success' as const,
icon: DollarSign,
icon: Euro,
},
];
@@ -245,27 +254,12 @@ const ProcurementPage: React.FC = () => {
title="Planificación de Compras"
description="Administra planes de compras, requerimientos y análisis de procurement"
actions={[
{
id: "export",
label: "Exportar",
variant: "outline" as const,
icon: Download,
onClick: () => console.log('Export procurement data')
},
{
id: "generate",
label: "Generar Plan",
variant: "primary" as const,
icon: Plus,
onClick: () => generatePlanMutation.mutate({
tenantId,
request: {
force_regenerate: false,
planning_horizon_days: 14,
include_safety_stock: true,
safety_stock_percentage: 20
}
})
onClick: () => setShowGeneratePlanModal(true)
},
{
id: "trigger",
@@ -275,7 +269,7 @@ const ProcurementPage: React.FC = () => {
onClick: () => {
triggerSchedulerMutation.mutate(tenantId, {
onSuccess: (data) => {
console.log('✅ Scheduler ejecutado exitosamente:', data.message);
// Scheduler executed successfully
// Show success notification (if you have a notification system)
// toast.success(data.message);
},
@@ -314,10 +308,6 @@ const ProcurementPage: React.FC = () => {
className="w-full"
/>
</div>
<Button variant="outline" onClick={() => console.log('Export filtered')}>
<Download className="w-4 h-4 mr-2" />
Exportar
</Button>
</div>
</Card>
@@ -610,7 +600,7 @@ const ProcurementPage: React.FC = () => {
icon: Eye,
variant: 'primary',
priority: 'primary',
onClick: () => console.log('View requirement details', requirement)
onClick: () => handleViewRequirementDetails(requirement)
},
...(requirement.purchase_order_number ? [
{
@@ -618,7 +608,9 @@ const ProcurementPage: React.FC = () => {
icon: Eye,
variant: 'outline' as const,
priority: 'secondary' as const,
onClick: () => console.log('View PO', requirement.purchase_order_number)
onClick: () => {
// TODO: Open purchase order details
}
}
] : [
{
@@ -626,7 +618,9 @@ const ProcurementPage: React.FC = () => {
icon: Plus,
variant: 'outline' as const,
priority: 'secondary' as const,
onClick: () => console.log('Create PO for', requirement)
onClick: () => {
// TODO: Create purchase order for requirement
}
}
]),
{
@@ -634,7 +628,9 @@ const ProcurementPage: React.FC = () => {
icon: Building2,
variant: 'outline' as const,
priority: 'secondary' as const,
onClick: () => console.log('Assign supplier')
onClick: () => {
// TODO: Open supplier assignment modal
}
}
]}
/>
@@ -724,7 +720,7 @@ const ProcurementPage: React.FC = () => {
},
{
title: 'Información Financiera',
icon: DollarSign,
icon: Euro,
fields: [
{
label: 'Costo Estimado Total',
@@ -780,10 +776,412 @@ const ProcurementPage: React.FC = () => {
}] : [])
]}
onEdit={() => {
console.log('Editing procurement plan:', selectedPlan.id);
// TODO: Implement plan editing functionality
}}
/>
)}
{/* Generate Plan Modal */}
{showGeneratePlanModal && (
<div className="fixed top-[var(--header-height)] left-0 right-0 bottom-0 bg-black bg-opacity-50 flex items-center justify-center z-40">
<div className="bg-[var(--bg-primary)] rounded-lg shadow-xl max-w-2xl w-full max-h-[90vh] overflow-hidden mx-4">
{/* Header */}
<div className="flex items-center justify-between p-6 border-b border-[var(--border-primary)]">
<div className="flex items-center space-x-3">
<div className="flex items-center justify-center w-8 h-8 rounded-full bg-blue-100">
<Plus className="w-5 h-5 text-blue-600" />
</div>
<div>
<h2 className="text-lg font-semibold text-[var(--text-primary)]">
Generar Plan de Compras
</h2>
<p className="text-sm text-[var(--text-secondary)]">
Configura los parámetros para generar un nuevo plan
</p>
</div>
</div>
<Button
variant="outline"
onClick={() => setShowGeneratePlanModal(false)}
className="p-2"
>
<X className="w-4 h-4" />
</Button>
</div>
{/* Content */}
<div className="p-6 overflow-y-auto max-h-[calc(90vh-200px)]">
<div className="space-y-6">
{/* Plan Date */}
<div>
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
Fecha del Plan
</label>
<Input
type="date"
value={generatePlanForm.plan_date}
onChange={(e) => setGeneratePlanForm(prev => ({ ...prev, plan_date: e.target.value }))}
className="w-full"
/>
<p className="text-xs text-[var(--text-tertiary)] mt-1">
Fecha para la cual se generará el plan de compras
</p>
</div>
{/* Planning Horizon */}
<div>
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
Horizonte de Planificación (días)
</label>
<Input
type="number"
min="1"
max="365"
value={generatePlanForm.planning_horizon_days}
onChange={(e) => setGeneratePlanForm(prev => ({ ...prev, planning_horizon_days: parseInt(e.target.value) }))}
className="w-full"
/>
<p className="text-xs text-[var(--text-tertiary)] mt-1">
Número de días a considerar en la planificación (1-365)
</p>
</div>
{/* Safety Stock */}
<div className="space-y-4">
<div className="flex items-center space-x-3">
<input
type="checkbox"
id="include_safety_stock"
checked={generatePlanForm.include_safety_stock}
onChange={(e) => setGeneratePlanForm(prev => ({ ...prev, include_safety_stock: e.target.checked }))}
className="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500"
/>
<label htmlFor="include_safety_stock" className="text-sm font-medium text-[var(--text-secondary)]">
Incluir Stock de Seguridad
</label>
</div>
{generatePlanForm.include_safety_stock && (
<div>
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
Porcentaje de Stock de Seguridad (%)
</label>
<Input
type="number"
min="0"
max="100"
value={generatePlanForm.safety_stock_percentage}
onChange={(e) => setGeneratePlanForm(prev => ({ ...prev, safety_stock_percentage: parseInt(e.target.value) }))}
className="w-full"
/>
<p className="text-xs text-[var(--text-tertiary)] mt-1">
Porcentaje adicional para stock de seguridad (0-100%)
</p>
</div>
)}
</div>
{/* Force Regenerate */}
<div className="flex items-center space-x-3">
<input
type="checkbox"
id="force_regenerate"
checked={generatePlanForm.force_regenerate}
onChange={(e) => setGeneratePlanForm(prev => ({ ...prev, force_regenerate: e.target.checked }))}
className="w-4 h-4 text-red-600 bg-gray-100 border-gray-300 rounded focus:ring-red-500"
/>
<label htmlFor="force_regenerate" className="text-sm font-medium text-[var(--text-secondary)]">
Forzar Regeneración
</label>
</div>
<p className="text-xs text-[var(--text-tertiary)] ml-7">
Si ya existe un plan para esta fecha, regenerarlo (esto eliminará el plan existente)
</p>
</div>
</div>
{/* Footer */}
<div className="flex justify-end space-x-3 p-6 border-t border-[var(--border-primary)]">
<Button
variant="outline"
onClick={() => setShowGeneratePlanModal(false)}
disabled={generatePlanMutation.isPending}
>
Cancelar
</Button>
<Button
variant="primary"
onClick={() => {
generatePlanMutation.mutate({
tenantId,
request: {
plan_date: generatePlanForm.plan_date,
planning_horizon_days: generatePlanForm.planning_horizon_days,
include_safety_stock: generatePlanForm.include_safety_stock,
safety_stock_percentage: generatePlanForm.safety_stock_percentage,
force_regenerate: generatePlanForm.force_regenerate
}
}, {
onSuccess: () => {
setShowGeneratePlanModal(false);
// Reset form to defaults
setGeneratePlanForm({
plan_date: new Date().toISOString().split('T')[0],
planning_horizon_days: 14,
include_safety_stock: true,
safety_stock_percentage: 20,
force_regenerate: false
});
}
});
}}
disabled={generatePlanMutation.isPending}
>
{generatePlanMutation.isPending ? (
<>
<Loader className="w-4 h-4 mr-2 animate-spin" />
Generando...
</>
) : (
<>
<Plus className="w-4 h-4 mr-2" />
Generar Plan
</>
)}
</Button>
</div>
</div>
</div>
)}
{/* Requirement Details Modal */}
{showRequirementDetailsModal && selectedRequirement && (
<div className="fixed top-[var(--header-height)] left-0 right-0 bottom-0 bg-black bg-opacity-50 flex items-center justify-center z-40">
<div className="bg-[var(--bg-primary)] rounded-lg shadow-xl max-w-4xl w-full max-h-[90vh] overflow-hidden mx-4">
{/* Header */}
<div className="flex items-center justify-between p-6 border-b border-[var(--border-primary)]">
<div className="flex items-center space-x-3">
<div className="flex items-center justify-center w-8 h-8 rounded-full bg-blue-100">
<Eye className="w-5 h-5 text-blue-600" />
</div>
<div>
<h2 className="text-lg font-semibold text-[var(--text-primary)]">
Detalles del Requerimiento
</h2>
<p className="text-sm text-[var(--text-secondary)]">
{selectedRequirement.product_name}
</p>
</div>
</div>
<Button
variant="outline"
onClick={() => setShowRequirementDetailsModal(false)}
className="p-2"
>
<X className="w-4 h-4" />
</Button>
</div>
{/* Content */}
<div className="p-6 overflow-y-auto max-h-[calc(90vh-200px)]">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Product Information */}
<div className="space-y-4">
<h3 className="text-md font-semibold text-[var(--text-primary)] border-b pb-2">
Información del Producto
</h3>
<div className="space-y-2">
<div>
<label className="text-sm font-medium text-[var(--text-secondary)]">Nombre</label>
<p className="text-[var(--text-primary)]">{selectedRequirement.product_name}</p>
</div>
<div>
<label className="text-sm font-medium text-[var(--text-secondary)]">SKU</label>
<p className="text-[var(--text-primary)]">{selectedRequirement.product_sku || 'N/A'}</p>
</div>
<div>
<label className="text-sm font-medium text-[var(--text-secondary)]">Categoría</label>
<p className="text-[var(--text-primary)]">{selectedRequirement.product_category || 'N/A'}</p>
</div>
<div>
<label className="text-sm font-medium text-[var(--text-secondary)]">Tipo</label>
<p className="text-[var(--text-primary)]">{selectedRequirement.product_type}</p>
</div>
</div>
</div>
{/* Quantities */}
<div className="space-y-4">
<h3 className="text-md font-semibold text-[var(--text-primary)] border-b pb-2">
Cantidades
</h3>
<div className="space-y-2">
<div>
<label className="text-sm font-medium text-[var(--text-secondary)]">Cantidad Requerida</label>
<p className="text-[var(--text-primary)]">{selectedRequirement.required_quantity} {selectedRequirement.unit_of_measure}</p>
</div>
<div>
<label className="text-sm font-medium text-[var(--text-secondary)]">Stock de Seguridad</label>
<p className="text-[var(--text-primary)]">{selectedRequirement.safety_stock_quantity} {selectedRequirement.unit_of_measure}</p>
</div>
<div>
<label className="text-sm font-medium text-[var(--text-secondary)]">Stock Actual</label>
<p className="text-[var(--text-primary)]">{selectedRequirement.current_stock_level} {selectedRequirement.unit_of_measure}</p>
</div>
<div>
<label className="text-sm font-medium text-[var(--text-secondary)]">Requerimiento Neto</label>
<p className="text-[var(--text-primary)] font-semibold">{selectedRequirement.net_requirement} {selectedRequirement.unit_of_measure}</p>
</div>
</div>
</div>
{/* Costs */}
<div className="space-y-4">
<h3 className="text-md font-semibold text-[var(--text-primary)] border-b pb-2">
Costos
</h3>
<div className="space-y-2">
<div>
<label className="text-sm font-medium text-[var(--text-secondary)]">Costo Unitario Estimado</label>
<p className="text-[var(--text-primary)]">{selectedRequirement.estimated_unit_cost || 'N/A'}</p>
</div>
<div>
<label className="text-sm font-medium text-[var(--text-secondary)]">Costo Total Estimado</label>
<p className="text-[var(--text-primary)] font-semibold">{selectedRequirement.estimated_total_cost || 'N/A'}</p>
</div>
{selectedRequirement.last_purchase_cost && (
<div>
<label className="text-sm font-medium text-[var(--text-secondary)]">Último Precio de Compra</label>
<p className="text-[var(--text-primary)]">{selectedRequirement.last_purchase_cost}</p>
</div>
)}
</div>
</div>
{/* Dates & Timeline */}
<div className="space-y-4">
<h3 className="text-md font-semibold text-[var(--text-primary)] border-b pb-2">
Fechas
</h3>
<div className="space-y-2">
<div>
<label className="text-sm font-medium text-[var(--text-secondary)]">Requerido Para</label>
<p className="text-[var(--text-primary)]">{selectedRequirement.required_by_date}</p>
</div>
{selectedRequirement.suggested_order_date && (
<div>
<label className="text-sm font-medium text-[var(--text-secondary)]">Fecha Sugerida de Pedido</label>
<p className="text-[var(--text-primary)]">{selectedRequirement.suggested_order_date}</p>
</div>
)}
{selectedRequirement.latest_order_date && (
<div>
<label className="text-sm font-medium text-[var(--text-secondary)]">Fecha Límite de Pedido</label>
<p className="text-[var(--text-primary)] text-red-600">{selectedRequirement.latest_order_date}</p>
</div>
)}
</div>
</div>
{/* Status & Priority */}
<div className="space-y-4">
<h3 className="text-md font-semibold text-[var(--text-primary)] border-b pb-2">
Estado y Prioridad
</h3>
<div className="space-y-2">
<div>
<label className="text-sm font-medium text-[var(--text-secondary)]">Estado</label>
<span className={`inline-flex px-2 py-1 text-xs font-medium rounded-full ${
selectedRequirement.status === 'pending' ? 'bg-yellow-100 text-yellow-800' :
selectedRequirement.status === 'approved' ? 'bg-green-100 text-green-800' :
selectedRequirement.status === 'ordered' ? 'bg-blue-100 text-blue-800' :
'bg-gray-100 text-gray-800'
}`}>
{selectedRequirement.status}
</span>
</div>
<div>
<label className="text-sm font-medium text-[var(--text-secondary)]">Prioridad</label>
<span className={`inline-flex px-2 py-1 text-xs font-medium rounded-full ${
selectedRequirement.priority === 'critical' ? 'bg-red-100 text-red-800' :
selectedRequirement.priority === 'high' ? 'bg-orange-100 text-orange-800' :
'bg-green-100 text-green-800'
}`}>
{selectedRequirement.priority}
</span>
</div>
<div>
<label className="text-sm font-medium text-[var(--text-secondary)]">Nivel de Riesgo</label>
<span className={`inline-flex px-2 py-1 text-xs font-medium rounded-full ${
selectedRequirement.risk_level === 'high' ? 'bg-red-100 text-red-800' :
selectedRequirement.risk_level === 'medium' ? 'bg-yellow-100 text-yellow-800' :
'bg-green-100 text-green-800'
}`}>
{selectedRequirement.risk_level}
</span>
</div>
</div>
</div>
{/* Supplier Information */}
{selectedRequirement.supplier_name && (
<div className="space-y-4">
<h3 className="text-md font-semibold text-[var(--text-primary)] border-b pb-2">
Proveedor
</h3>
<div className="space-y-2">
<div>
<label className="text-sm font-medium text-[var(--text-secondary)]">Nombre</label>
<p className="text-[var(--text-primary)]">{selectedRequirement.supplier_name}</p>
</div>
{selectedRequirement.supplier_lead_time_days && (
<div>
<label className="text-sm font-medium text-[var(--text-secondary)]">Tiempo de Entrega</label>
<p className="text-[var(--text-primary)]">{selectedRequirement.supplier_lead_time_days} días</p>
</div>
)}
</div>
</div>
)}
</div>
{/* Special Requirements */}
{selectedRequirement.special_requirements && (
<div className="mt-6 space-y-2">
<h3 className="text-md font-semibold text-[var(--text-primary)] border-b pb-2">
Requerimientos Especiales
</h3>
<p className="text-[var(--text-primary)] bg-gray-50 p-3 rounded-lg">
{selectedRequirement.special_requirements}
</p>
</div>
)}
</div>
{/* Footer */}
<div className="flex justify-end space-x-3 p-6 border-t border-[var(--border-primary)]">
<Button
variant="outline"
onClick={() => setShowRequirementDetailsModal(false)}
>
Cerrar
</Button>
{selectedRequirement.status === 'pending' && (
<Button
variant="primary"
onClick={() => {
// TODO: Implement approval functionality
setShowRequirementDetailsModal(false);
}}
>
<CheckCircle className="w-4 h-4 mr-2" />
Aprobar
</Button>
)}
</div>
</div>
</div>
)}
</div>
);
};

View File

@@ -1,5 +1,5 @@
import React, { useState, useMemo } from 'react';
import { Plus, Star, Clock, DollarSign, Package, Eye, Edit, ChefHat, Timer, Euro } from 'lucide-react';
import { Plus, Star, Clock, Euro, Package, Eye, Edit, ChefHat, Timer } from 'lucide-react';
import { Button, Input, Card, StatsGrid, StatusCard, getStatusColor, StatusModal } from '../../../../components/ui';
import { LoadingSpinner } from '../../../../components/shared';
import { formatters } from '../../../../components/ui/Stats/StatsPresets';
@@ -321,7 +321,7 @@ const RecipesPage: React.FC = () => {
},
{
title: 'Análisis Financiero',
icon: DollarSign,
icon: Euro,
fields: [
{
label: 'Costo estimado por unidad',

View File

@@ -16,7 +16,7 @@ import {
Play,
Calendar,
Clock,
DollarSign,
Euro,
Package,
PieChart,
Settings
@@ -248,7 +248,7 @@ const LandingPage: React.FC = () => {
<div className="text-center p-6 bg-[var(--bg-primary)] rounded-xl border border-[var(--border-primary)]">
<div className="w-12 h-12 bg-[var(--color-secondary)]/10 rounded-lg flex items-center justify-center mx-auto mb-4">
<DollarSign className="w-6 h-6 text-[var(--color-secondary)]" />
<Euro className="w-6 h-6 text-[var(--color-secondary)]" />
</div>
<h4 className="font-semibold text-[var(--text-primary)]">POS Integrado</h4>
<p className="text-sm text-[var(--text-secondary)] mt-2">Sistema de ventas completo y fácil de usar</p>