diff --git a/backlog/features.json b/backlog/features.json
index 98d1b66..eed9d1f 100644
--- a/backlog/features.json
+++ b/backlog/features.json
@@ -3830,13 +3830,15 @@
"No regressions in the existing Prices tab or product editor",
"verify.sh is green"
],
- "status": "pending",
+ "status": "done",
"created_at": "2026-08-19",
"gates": {
- "reviewer": false,
- "security": false,
- "qa": false
- }
+ "reviewer": true,
+ "security": true,
+ "qa": true,
+ "close": true
+ },
+ "completed_at": "2026-08-20T04:04:22Z"
},
{
"id": "F-083",
diff --git a/project/apps/admin/src/app/(dashboard)/inventory/page.tsx b/project/apps/admin/src/app/(dashboard)/inventory/page.tsx
index b29e164..35b53b1 100644
--- a/project/apps/admin/src/app/(dashboard)/inventory/page.tsx
+++ b/project/apps/admin/src/app/(dashboard)/inventory/page.tsx
@@ -1,7 +1,7 @@
'use client';
import { useState, useEffect, useCallback } from 'react';
import { productsApi, inventoryApi } from '@/lib/api-client';
-import type { Product, ProductVariant, StockAvailability } from '@/types';
+import type { ProductVariant, StockAvailability } from '@/types';
interface VariantRow {
productId: string;
@@ -140,6 +140,56 @@ export default function InventoryPage() {
}
};
+ // Save Stock inline (used by Stock cell on blur/Enter)
+ const saveStockInline = async (variantId: string, value: string) => {
+ const qty = parseInt(value, 10);
+ if (isNaN(qty) || qty < 0) {
+ setRows((prev) =>
+ prev.map((r) =>
+ r.variant.id === variantId
+ ? { ...r, editing: false, editValue: String(r.stock?.availableQuantity ?? 0), msg: 'Error' }
+ : r,
+ ),
+ );
+ return;
+ }
+ setRows((prev) => prev.map((r) => (r.variant.id === variantId ? { ...r, saving: true } : r)));
+ try {
+ const result = await inventoryApi.setStock(variantId, qty);
+ setRows((prev) =>
+ prev.map((r) =>
+ r.variant.id === variantId
+ ? {
+ ...r,
+ stock: { available: result.available > 0, availableQuantity: result.available },
+ editing: false,
+ saving: false,
+ editValue: String(result.available),
+ msg: '✓ Guardado',
+ }
+ : r,
+ ),
+ );
+ setTimeout(() => {
+ setRows((prev) => prev.map((r) => (r.variant.id === variantId ? { ...r, msg: '' } : r)));
+ }, 3000);
+ } catch {
+ setRows((prev) =>
+ prev.map((r) =>
+ r.variant.id === variantId
+ ? {
+ ...r,
+ saving: false,
+ editing: false,
+ editValue: String(r.stock?.availableQuantity ?? 0),
+ msg: 'Error',
+ }
+ : r,
+ ),
+ );
+ }
+ };
+
// Filter rows
const filtered = rows.filter((r) => {
if (filter === 'in_stock') return (r.stock?.availableQuantity ?? 0) >= 5;
@@ -302,8 +352,9 @@ export default function InventoryPage() {
{row.loading ? (
—
) : row.editing ? (
-
+
-
-
+ className="w-20 px-2 py-1 border border-gray-300 rounded-lg text-sm focus:ring-1 focus:ring-[#2D6A4F] outline-none"
+ />
+ {row.msg && (
+
+ {row.msg}
+
+ )}
) : (