Simplify the onboardinf flow components 2

This commit is contained in:
Urtzi Alfaro
2025-09-08 21:44:04 +02:00
parent 2e1e696cb5
commit c8b1a941f8
6 changed files with 726 additions and 269 deletions

View File

@@ -24,18 +24,28 @@ interface ProgressState {
interface InventoryItem {
suggestion_id: string;
original_name: string;
suggested_name: string;
product_type: string;
category: string;
unit_of_measure: string;
selected: boolean;
stock_quantity: number;
expiration_days: number;
cost_per_unit: number;
confidence_score: number;
estimated_shelf_life_days?: number;
requires_refrigeration: boolean;
requires_freezing: boolean;
is_seasonal: boolean;
suggested_supplier?: string;
notes?: string;
sales_data?: {
total_quantity: number;
average_daily_sales: number;
peak_day: string;
frequency: number;
};
// UI-specific fields
selected: boolean;
stock_quantity: number;
cost_per_unit: number;
}
export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
@@ -131,15 +141,9 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
setProgressState({ stage: 'analyzing', progress: 25, message: 'Analizando productos de ventas...' });
try {
// Extract product data from validation result
const products = validationResult.product_summary?.map((product: any) => ({
product_name: product.name,
sales_volume: product.total_quantity,
sales_data: {
total_quantity: product.total_quantity,
average_daily_sales: product.average_daily_sales,
frequency: product.frequency
}
// Extract product data from validation result - use the exact backend structure
const products = validationResult.product_list?.map((productName: string) => ({
product_name: productName
})) || [];
if (products.length === 0) {
@@ -158,7 +162,7 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
setProgressState({ stage: 'preparing', progress: 75, message: 'Preparando sugerencias de inventario...' });
// Convert API response to InventoryItem format
// Convert API response to InventoryItem format - use exact backend structure plus UI fields
const items: InventoryItem[] = suggestions.map(suggestion => {
// Calculate default stock quantity based on sales data
const defaultStock = Math.max(
@@ -172,19 +176,25 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
3.0;
return {
// Exact backend fields
suggestion_id: suggestion.suggestion_id,
original_name: suggestion.original_name,
suggested_name: suggestion.suggested_name,
product_type: suggestion.product_type,
category: suggestion.category,
unit_of_measure: suggestion.unit_of_measure,
selected: suggestion.confidence_score > 0.7, // Auto-select high confidence items
stock_quantity: defaultStock,
expiration_days: suggestion.estimated_shelf_life_days || 30,
cost_per_unit: estimatedCost,
confidence_score: suggestion.confidence_score,
estimated_shelf_life_days: suggestion.estimated_shelf_life_days,
requires_refrigeration: suggestion.requires_refrigeration,
requires_freezing: suggestion.requires_freezing,
is_seasonal: suggestion.is_seasonal,
notes: suggestion.notes
suggested_supplier: suggestion.suggested_supplier,
notes: suggestion.notes,
sales_data: suggestion.sales_data,
// UI-specific fields
selected: suggestion.confidence_score > 0.7, // Auto-select high confidence items
stock_quantity: defaultStock,
cost_per_unit: estimatedCost
};
});
@@ -241,18 +251,31 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
const createdIngredients = [];
for (const item of selectedItems) {
// Ensure reorder_point > minimum_stock_level as required by backend validation
const minimumStock = Math.max(1, Math.ceil(item.stock_quantity * 0.2));
const calculatedReorderPoint = Math.ceil(item.stock_quantity * 0.3);
const reorderPoint = Math.max(minimumStock + 2, calculatedReorderPoint, minimumStock + 1);
console.log(`📊 Inventory validation for "${item.suggested_name}":`, {
stockQuantity: item.stock_quantity,
minimumStock,
calculatedReorderPoint,
finalReorderPoint: reorderPoint,
isValid: reorderPoint > minimumStock
});
const ingredientData = {
name: item.suggested_name,
category: item.category,
unit_of_measure: item.unit_of_measure,
minimum_stock_level: Math.ceil(item.stock_quantity * 0.2),
maximum_stock_level: item.stock_quantity * 2,
reorder_point: Math.ceil(item.stock_quantity * 0.3),
shelf_life_days: item.expiration_days,
low_stock_threshold: minimumStock,
max_stock_level: item.stock_quantity * 2,
reorder_point: reorderPoint,
shelf_life_days: item.estimated_shelf_life_days || 30,
requires_refrigeration: item.requires_refrigeration,
requires_freezing: item.requires_freezing,
is_seasonal: item.is_seasonal,
cost_per_unit: item.cost_per_unit,
average_cost: item.cost_per_unit,
notes: item.notes || `Creado durante onboarding - Confianza: ${Math.round(item.confidence_score * 100)}%`
};
@@ -338,12 +361,12 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
{/* Summary */}
<div className="bg-[var(--bg-secondary)] rounded-lg p-4">
<div className="flex justify-between items-center">
<div>
<p className="font-medium">
<div className="flex flex-col sm:flex-row sm:justify-between sm:items-center space-y-3 sm:space-y-0">
<div className="text-center sm:text-left">
<p className="font-medium text-sm sm:text-base">
{selectedCount} de {inventoryItems.length} artículos seleccionados
</p>
<p className="text-sm text-[var(--text-secondary)]">
<p className="text-xs sm:text-sm text-[var(--text-secondary)]">
Los artículos con alta confianza están preseleccionados
</p>
</div>
@@ -351,6 +374,7 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
variant="outline"
size="sm"
onClick={handleSelectAll}
className="w-full sm:w-auto"
>
{allSelected ? 'Deseleccionar Todos' : 'Seleccionar Todos'}
</Button>
@@ -361,7 +385,7 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
<div className="space-y-4 max-h-96 overflow-y-auto">
{inventoryItems.map((item) => (
<div
key={item.id}
key={item.suggestion_id}
className={`border rounded-lg p-4 transition-colors ${
item.selected
? 'border-[var(--color-primary)] bg-[var(--color-primary)]/5'
@@ -373,7 +397,7 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
<input
type="checkbox"
checked={item.selected}
onChange={() => handleToggleSelection(item.id)}
onChange={() => handleToggleSelection(item.suggestion_id)}
className="w-4 h-4 text-[var(--color-primary)] border-[var(--border-secondary)] rounded focus:ring-[var(--color-primary)]"
/>
</div>
@@ -381,7 +405,7 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
<div className="flex-1 space-y-3">
<div>
<h3 className="font-medium text-[var(--text-primary)]">
{item.name}
{item.suggested_name}
</h3>
<p className="text-sm text-[var(--text-secondary)]">
{item.category} Unidad: {item.unit_of_measure}
@@ -395,18 +419,28 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
Requiere refrigeración
</span>
)}
{item.requires_freezing && (
<span className="text-xs bg-cyan-100 text-cyan-800 px-2 py-1 rounded">
Requiere congelación
</span>
)}
{item.is_seasonal && (
<span className="text-xs bg-green-100 text-green-800 px-2 py-1 rounded">
Producto estacional
</span>
)}
</div>
</div>
{item.selected && (
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 pt-3 border-t border-[var(--border-secondary)]">
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4 pt-3 border-t border-[var(--border-secondary)]">
<Input
label="Stock Inicial"
type="number"
min="0"
value={item.stock_quantity.toString()}
onChange={(e) => handleUpdateItem(
item.id,
item.suggestion_id,
'stock_quantity',
Number(e.target.value)
)}
@@ -419,7 +453,7 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
step="0.01"
value={item.cost_per_unit.toString()}
onChange={(e) => handleUpdateItem(
item.id,
item.suggestion_id,
'cost_per_unit',
Number(e.target.value)
)}
@@ -429,13 +463,14 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
label="Días de Caducidad"
type="number"
min="1"
value={item.expiration_days.toString()}
value={(item.estimated_shelf_life_days || 30).toString()}
onChange={(e) => handleUpdateItem(
item.id,
'expiration_days',
item.suggestion_id,
'estimated_shelf_life_days',
Number(e.target.value)
)}
size="sm"
className="sm:col-span-2 lg:col-span-1"
/>
</div>
)}
@@ -452,10 +487,11 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
)}
{/* Actions */}
<div className="flex justify-between">
<div className="flex flex-col sm:flex-row justify-between gap-3 sm:gap-0">
<Button
variant="outline"
onClick={() => setShowInventoryStep(false)}
className="order-2 sm:order-1 w-full sm:w-auto"
>
Volver
</Button>
@@ -466,8 +502,14 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
loadingText="Creando Inventario..."
size="lg"
disabled={selectedCount === 0}
className="order-1 sm:order-2 w-full sm:w-auto"
>
Crear {selectedCount} Artículo{selectedCount !== 1 ? 's' : ''} de Inventario
<span className="hidden sm:inline">
Crear {selectedCount} Artículo{selectedCount !== 1 ? 's' : ''} de Inventario
</span>
<span className="sm:hidden">
Crear {selectedCount} Artículo{selectedCount !== 1 ? 's' : ''}
</span>
</Button>
</div>
</div>
@@ -574,7 +616,9 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
<p className="font-medium text-[var(--color-warning)]">Warnings:</p>
<ul className="list-disc list-inside">
{validationResult.warnings.map((warning, index) => (
<li key={index} className="text-[var(--color-warning)]">{warning}</li>
<li key={index} className="text-[var(--color-warning)]">
{typeof warning === 'string' ? warning : JSON.stringify(warning)}
</li>
))}
</ul>
</div>
@@ -591,23 +635,26 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
)}
{/* Actions */}
<div className="flex justify-between">
<div className="flex flex-col sm:flex-row justify-between gap-3 sm:gap-0">
<Button
variant="outline"
onClick={onPrevious}
disabled={isFirstStep}
className="order-2 sm:order-1 w-full sm:w-auto"
>
Previous
Anterior
</Button>
<div className="space-x-3">
<div className="flex flex-col sm:flex-row gap-3 order-1 sm:order-2">
{selectedFile && !validationResult && (
<Button
onClick={handleValidateFile}
isLoading={isValidating}
loadingText="Validating..."
loadingText="Validando..."
size="lg"
className="w-full sm:w-auto"
>
Validate File
Validar Archivo
</Button>
)}
@@ -615,8 +662,9 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
<Button
onClick={handleContinue}
size="lg"
className="w-full sm:w-auto"
>
Continue with This Data
Continuar con estos Datos
</Button>
)}
</div>