Fix inline edit form positioning in AI inventory configuration

**Issue:** When clicking "Edit" on an ingredient in the list, the edit form
appeared at the bottom of the page after all ingredients, forcing users to
scroll down. This was poor UX especially with 10+ ingredients.

**Solution:** Moved edit form to appear inline directly below the ingredient
being edited.

**Changes Made:**

1. **Inline Edit Form Display**
   - Edit form now renders inside the ingredient map loop
   - Shows conditionally when `editingId === item.id`
   - Appears immediately below the specific ingredient being edited
   - Location: frontend/src/components/domain/onboarding/steps/UploadSalesDataStep.tsx:834-1029

2. **Hide Ingredient Card While Editing**
   - Ingredient card (with stock lots) hidden when that ingredient is being edited
   - Condition: `{editingId !== item.id && (...)}`
   - Prevents duplication of information
   - Location: lines 629-832

3. **Separate Add Manually Form**
   - Bottom form now only shows when adding new ingredient (not editing)
   - Condition changed from `{isAdding ? (` to `{isAdding && !editingId ? (`
   - Title simplified to "Agregar Ingrediente Manualmente"
   - Button label simplified to "Agregar a Lista"
   - Location: lines 1042-1237

**User Experience:**
Before: Edit button → scroll to bottom → edit form → scroll back up
After: Edit button → form appears right there → edit → save → continues

**Structure:**
```jsx
{inventoryItems.map((item) => (
  <div key={item.id}>
    {editingId !== item.id && (
      <>
        {/* Ingredient card */}
        {/* Stock lots section */}
      </>
    )}

    {editingId === item.id && (
      {/* Inline edit form */}
    )}
  </div>
))}

{isAdding && !editingId && (
  {/* Add manually form at bottom */}
)}
```

**Build Status:** ✓ Successful in 20.61s
This commit is contained in:
Claude
2025-11-06 21:45:38 +00:00
parent 011843dff9
commit e7c26b3cfc

View File

@@ -624,10 +624,10 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
{inventoryItems.length > 0 ? (
<div className="space-y-3">
{inventoryItems.map((item) => (
<div
key={item.id}
className="p-4 bg-[var(--bg-secondary)] border border-[var(--border-secondary)] rounded-lg"
>
<div key={item.id}>
{/* Show ingredient card only if NOT editing this item */}
{editingId !== item.id && (
<div className="p-4 bg-[var(--bg-secondary)] border border-[var(--border-secondary)] rounded-lg">
<div className="flex items-start justify-between">
<div className="flex-1">
<div className="flex items-center gap-2">
@@ -829,22 +829,13 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
);
})()}
</div>
))}
</div>
) : (
<div className="text-center py-8 border border-dashed border-[var(--border-secondary)] rounded-lg">
<p className="text-[var(--text-tertiary)] text-sm">
No hay ingredientes en la lista todavía
</p>
</div>
)}
</div>
{/* Add/Edit Form */}
{isAdding ? (
{/* Inline Edit Form - show only when editing this specific ingredient */}
{editingId === item.id && (
<div className="border-2 border-[var(--color-primary)] rounded-lg p-4 bg-gradient-to-br from-[var(--color-primary)]/5 to-transparent">
<h4 className="font-semibold text-[var(--text-primary)] mb-4">
{editingId ? 'Editar Ingrediente' : 'Agregar Ingrediente Manualmente'}
Editar Ingrediente
</h4>
<form onSubmit={handleSubmitForm} className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
@@ -1023,7 +1014,215 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
type="submit"
className="px-4 py-2 bg-[var(--color-primary)] text-white rounded-lg hover:bg-[var(--color-primary-dark)] transition-colors font-medium"
>
{editingId ? 'Guardar Cambios' : 'Agregar a Lista'}
Guardar Cambios
</button>
<button
type="button"
onClick={resetForm}
className="px-4 py-2 text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-primary)] rounded-lg transition-colors"
>
Cancelar
</button>
</div>
</form>
</div>
)}
</div>
))}
</div>
) : (
<div className="text-center py-8 border border-dashed border-[var(--border-secondary)] rounded-lg">
<p className="text-[var(--text-tertiary)] text-sm">
No hay ingredientes en la lista todavía
</p>
</div>
)}
</div>
{/* Add Manually Form - only show when adding new (not editing existing) */}
{isAdding && !editingId ? (
<div className="border-2 border-[var(--color-primary)] rounded-lg p-4 bg-gradient-to-br from-[var(--color-primary)]/5 to-transparent">
<h4 className="font-semibold text-[var(--text-primary)] mb-4">
Agregar Ingrediente Manualmente
</h4>
<form onSubmit={handleSubmitForm} className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-[var(--text-primary)] mb-1">
Nombre *
</label>
<input
type="text"
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
className="w-full px-3 py-2 bg-[var(--bg-primary)] border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] text-[var(--text-primary)]"
placeholder="Ej: Harina de trigo"
/>
{formErrors.name && (
<p className="text-xs text-[var(--color-error)] mt-1">{formErrors.name}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-[var(--text-primary)] mb-1">
Categoría *
</label>
<select
value={formData.category}
onChange={(e) => setFormData({ ...formData, category: e.target.value })}
className="w-full px-3 py-2 bg-[var(--bg-primary)] border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] text-[var(--text-primary)]"
>
<option value="">Seleccionar...</option>
{categoryOptions.map(cat => (
<option key={cat} value={cat}>{cat}</option>
))}
</select>
{formErrors.category && (
<p className="text-xs text-[var(--color-error)] mt-1">{formErrors.category}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-[var(--text-primary)] mb-1">
Unidad de Medida
</label>
<select
value={formData.unit_of_measure}
onChange={(e) => setFormData({ ...formData, unit_of_measure: e.target.value })}
className="w-full px-3 py-2 bg-[var(--bg-primary)] border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] text-[var(--text-primary)]"
>
{unitOptions.map(unit => (
<option key={unit} value={unit}>{unit}</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-[var(--text-primary)] mb-1">
Stock Inicial
</label>
<input
type="number"
min="0"
step="0.01"
value={formData.stock_quantity}
onChange={(e) => setFormData({ ...formData, stock_quantity: parseFloat(e.target.value) || 0 })}
className="w-full px-3 py-2 bg-[var(--bg-primary)] border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] text-[var(--text-primary)]"
/>
{formErrors.stock_quantity && (
<p className="text-xs text-[var(--color-error)] mt-1">{formErrors.stock_quantity}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-[var(--text-primary)] mb-1">
Costo por Unidad ()
</label>
<input
type="number"
min="0"
step="0.01"
value={formData.cost_per_unit}
onChange={(e) => setFormData({ ...formData, cost_per_unit: parseFloat(e.target.value) || 0 })}
className="w-full px-3 py-2 bg-[var(--bg-primary)] border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] text-[var(--text-primary)]"
/>
{formErrors.cost_per_unit && (
<p className="text-xs text-[var(--color-error)] mt-1">{formErrors.cost_per_unit}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-[var(--text-primary)] mb-1">
Días de Caducidad
</label>
<input
type="number"
min="1"
value={formData.estimated_shelf_life_days}
onChange={(e) => setFormData({ ...formData, estimated_shelf_life_days: parseInt(e.target.value) || 30 })}
className="w-full px-3 py-2 bg-[var(--bg-primary)] border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] text-[var(--text-primary)]"
/>
{formErrors.estimated_shelf_life_days && (
<p className="text-xs text-[var(--color-error)] mt-1">{formErrors.estimated_shelf_life_days}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-[var(--text-primary)] mb-1">
Stock Mínimo
</label>
<input
type="number"
min="0"
value={formData.low_stock_threshold}
onChange={(e) => setFormData({ ...formData, low_stock_threshold: parseInt(e.target.value) || 0 })}
className="w-full px-3 py-2 bg-[var(--bg-primary)] border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] text-[var(--text-primary)]"
/>
</div>
<div>
<label className="block text-sm font-medium text-[var(--text-primary)] mb-1">
Punto de Reorden
</label>
<input
type="number"
min="0"
value={formData.reorder_point}
onChange={(e) => setFormData({ ...formData, reorder_point: parseInt(e.target.value) || 0 })}
className="w-full px-3 py-2 bg-[var(--bg-primary)] border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] text-[var(--text-primary)]"
/>
</div>
</div>
<div className="flex items-center gap-4">
<label className="flex items-center gap-2 text-sm text-[var(--text-primary)]">
<input
type="checkbox"
checked={formData.requires_refrigeration}
onChange={(e) => setFormData({ ...formData, requires_refrigeration: e.target.checked })}
className="rounded"
/>
Requiere Refrigeración
</label>
<label className="flex items-center gap-2 text-sm text-[var(--text-primary)]">
<input
type="checkbox"
checked={formData.requires_freezing}
onChange={(e) => setFormData({ ...formData, requires_freezing: e.target.checked })}
className="rounded"
/>
Requiere Congelación
</label>
<label className="flex items-center gap-2 text-sm text-[var(--text-primary)]">
<input
type="checkbox"
checked={formData.is_seasonal}
onChange={(e) => setFormData({ ...formData, is_seasonal: e.target.checked })}
className="rounded"
/>
Estacional
</label>
</div>
<div>
<label className="block text-sm font-medium text-[var(--text-primary)] mb-1">
Notas (opcional)
</label>
<textarea
value={formData.notes}
onChange={(e) => setFormData({ ...formData, notes: e.target.value })}
rows={2}
className="w-full px-3 py-2 bg-[var(--bg-primary)] border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] text-[var(--text-primary)]"
placeholder="Información adicional..."
/>
</div>
<div className="flex gap-2 pt-2">
<button
type="submit"
className="px-4 py-2 bg-[var(--color-primary)] text-white rounded-lg hover:bg-[var(--color-primary-dark)] transition-colors font-medium"
>
Agregar a Lista
</button>
<button
type="button"