Simplify the onboardinf flow components 2
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user