From 8a222afb0e3e1ad7139c65806b2e0b3411d32394 Mon Sep 17 00:00:00 2001 From: daniel Date: Mon, 6 Jul 2026 09:54:25 +0200 Subject: [PATCH 1/3] chore: gitignore .worktrees/ for local parallel dev worktrees --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 7868101a..f80656a9 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,6 @@ apps/api/uploads/ # Entorno local personal (scripts, seeds, docker extra) — no subir al repo .local/ + +# Local git worktrees for parallel agent work — never commit +.worktrees/ From 4fa5f508cbdbe6dcb37d7c851202239de40b8eb7 Mon Sep 17 00:00:00 2001 From: daniel Date: Mon, 6 Jul 2026 10:09:16 +0200 Subject: [PATCH 2/3] =?UTF-8?q?fix(web):=20se=C3=B1alar=20fila=20inv=C3=A1?= =?UTF-8?q?lida=20en=20parseSupplyLines=20(#296)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit parseSupplyLines devolvía null sin decir qué línea fallaba, así que en el editor de inventario (#263, guardado estricto) un usuario con 20 filas solo veía "revisa el material" sin saber cuál. Ahora devuelve { items } | { invalidRow } con el índice de la primera fila inválida (-1 cuando el fallo no es de una fila concreta: JSON corrupto, raíz no-array, o lista vacía requerida). Se propaga el índice hasta la UI: InventoryField/SupplyLineList/ SupplyLineFields aceptan un invalidRowIndex opcional y resaltan esa fila (borde + aviso) en el editor de inventario. El resto de consumidores (pre-registro, petición, registrar, recepción) se adaptan a la nueva forma sin cambiar su comportamiento. --- .../[resourceId]/inventario/actions.ts | 20 +++- .../inventario/inventory-edit-form.tsx | 1 + apps/web/src/app/e/[slug]/peticion/actions.ts | 5 +- .../src/app/e/[slug]/pre-registro/actions.ts | 5 +- .../web/src/app/e/[slug]/recepcion/actions.ts | 8 +- .../web/src/app/e/[slug]/registrar/actions.ts | 5 +- .../e/[slug]/registrar/inventory-field.tsx | 9 ++ .../molecules/supply-line-fields.tsx | 18 ++- .../components/organisms/supply-line-list.tsx | 5 +- apps/web/src/i18n/messages/en.ts | 21 ++++ apps/web/src/i18n/messages/es.ts | 26 +++++ apps/web/src/lib/supply-lines.test.ts | 109 +++++++++++------- apps/web/src/lib/supply-lines.ts | 46 +++++--- 13 files changed, 206 insertions(+), 72 deletions(-) diff --git a/apps/web/src/app/e/[slug]/mis-puntos/[resourceId]/inventario/actions.ts b/apps/web/src/app/e/[slug]/mis-puntos/[resourceId]/inventario/actions.ts index 502d0cea..f5e5fb5a 100644 --- a/apps/web/src/app/e/[slug]/mis-puntos/[resourceId]/inventario/actions.ts +++ b/apps/web/src/app/e/[slug]/mis-puntos/[resourceId]/inventario/actions.ts @@ -14,7 +14,7 @@ type SupplyLineView = components['schemas']['SupplyLineResponseDto']; export type InventoryState = | { status: 'idle' } | { status: 'success' } - | { status: 'error'; message: string }; + | { status: 'error'; message: string; invalidRow?: number }; /** Owner/coordinator read of the point's full declared lines (null → notFound). */ export async function fetchMyInventory( @@ -63,13 +63,25 @@ export async function saveMyInventory( ); // allowEmpty: the owner can clear the inventory (empty list is a valid save). - const items = parseSupplyLines(formData.get('items'), { + const parsedItems = parseSupplyLines(formData.get('items'), { isValidCategory: (c) => validCategories.has(c), allowEmpty: true, }); - if (items === null) { - return { status: 'error', message: t.account.inventory_invalid_items }; + if ('invalidRow' in parsedItems) { + // invalidRow >= 0 means a specific row failed validation (#296): surface + // its 1-based position and let the caller highlight that row instead of + // making the owner hunt through a long inventory for the bad line. + const { invalidRow } = parsedItems; + return { + status: 'error', + message: + invalidRow >= 0 + ? t.account.inventory_invalid_row.replace('{n}', String(invalidRow + 1)) + : t.account.inventory_invalid_items, + ...(invalidRow >= 0 ? { invalidRow } : {}), + }; } + const { items } = parsedItems; const { response } = await api.PUT('/resources/{resourceId}/inventory', { params: { path: { resourceId } }, diff --git a/apps/web/src/app/e/[slug]/mis-puntos/[resourceId]/inventario/inventory-edit-form.tsx b/apps/web/src/app/e/[slug]/mis-puntos/[resourceId]/inventario/inventory-edit-form.tsx index 4358e382..e14b26ef 100644 --- a/apps/web/src/app/e/[slug]/mis-puntos/[resourceId]/inventario/inventory-edit-form.tsx +++ b/apps/web/src/app/e/[slug]/mis-puntos/[resourceId]/inventario/inventory-edit-form.tsx @@ -70,6 +70,7 @@ export function InventoryEditForm({ initialLines={initial.map(toLine)} strict allowAllCategories + invalidRowIndex={state.status === 'error' ? state.invalidRow : undefined} />