From b4b25551ff1c0cec0d2cad77d657a1e44efa6e10 Mon Sep 17 00:00:00 2001 From: Kamil Krzywda Date: Sun, 20 Sep 2026 18:07:18 +0000 Subject: [PATCH] =?UTF-8?q?Fix=20ignore=20route=20slug=20collision=20([pac?= =?UTF-8?q?kageId]=20=E2=86=92=20[id])=20=E2=80=94=20Next.js=20runtime=205?= =?UTF-8?q?00s=20(ISS-047=20part=20B)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 +- app/api/packages/{[packageId] => [id]}/ignore/route.ts | 6 +++--- e2e/api.spec.ts | 2 +- lib/openapi.test.ts | 2 +- lib/openapi.ts | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) rename app/api/packages/{[packageId] => [id]}/ignore/route.ts (76%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 182befa..60a64c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## v0.29.0 — 2026-09-20 14:27 - **Ignore a job (ISS-047, part B).** A player can now dismiss an open job so it leaves the market permanently; new random jobs spawn in its place via the existing top-up logic (`spawnOpenPackages` counts 'open', so the market refills to 10 on the next tick). New terminal package status `'ignored'` added to the status union/enum in `lib/db/schema.ts`, `lib/game/state.ts`, `lib/openapi.ts` (PackageState.status) and the e2e-local interface copy in `e2e/right-pane.spec.ts`. No DB migration — the column is plain TEXT with no CHECK constraint. New action `ignorePackage` (`lib/game/actions.ts`) next to abandon: 404 unknown id, 409 unless open ("only open packages can be ignored"), otherwise flips status to 'ignored' — no body, no penalty, nothing else changes -- **New route.** `POST /api/packages/{packageId}/ignore` (`app/api/packages/[packageId]/ignore/route.ts`) — no body, mirrors the abandon route exactly (same performAction wrapper, full state snapshot on success). The 13th public endpoint: OpenAPI spec gains the path + 'ignored' in PackageState.status; `lib/openapi.test.ts` and `e2e/api.spec.ts` path lists updated twelve → thirteen +- **New route.** `POST /api/packages/{id}/ignore` (`app/api/packages/[id]/ignore/route.ts`) — no body, mirrors the abandon route exactly (same performAction wrapper, full state snapshot on success). The 13th public endpoint: OpenAPI spec gains the path + 'ignored' in PackageState.status; `lib/openapi.test.ts` and `e2e/api.spec.ts` path lists updated twelve → thirteen - **UI.** Each open-package row in the Jobs tab gains a small "Ignore" button beside the Load controls (`components/RightPane.tsx`, testid `ignore-{pkg.id}`, disabled while the pane action is busy, calls runAction with no body). Ignored rows vanish from the list automatically — the section filters status==='open'; no toast (deriveEvents untouched) - Verified-safe consumers unchanged: expirePackages selects only open|loaded ('ignored' never expires), deliverPackages handles loaded/expired, RightPane filters 'open' - Tests: unit — new `ignorePackage` block in `lib/game/actions.test.ts` (open → ignored with cash/planeId untouched; 409 for each of loaded/delivered/expired/abandoned/already-ignored; 404 unknown id), `lib/sim/engine.test.ts` terminal-statuses test gains an 'ignored' row, `lib/openapi.test.ts` status enum list gains 'ignored'. E2E — new "Ignore removes an open job from the list permanently" test in `e2e/right-pane.spec.ts`: hook-created package renders with an enabled Ignore button, UI click → /api/state shows 'ignored', row gone from `[data-testid="open-packages"]`, second API ignore 409s diff --git a/app/api/packages/[packageId]/ignore/route.ts b/app/api/packages/[id]/ignore/route.ts similarity index 76% rename from app/api/packages/[packageId]/ignore/route.ts rename to app/api/packages/[id]/ignore/route.ts index ea4fc87..8c1a108 100644 --- a/app/api/packages/[packageId]/ignore/route.ts +++ b/app/api/packages/[id]/ignore/route.ts @@ -4,7 +4,7 @@ import { ignorePackage, performAction } from '@/lib/game/actions'; // leaves the market permanently; the top-up logic spawns a replacement on the next tick. No penalty, // nothing else changes. 404 unknown id; 409 unless open. On success returns the full state snapshot. -export async function POST(_req: Request, ctx: { params: Promise<{ packageId: string }> }) { - const { packageId } = await ctx.params; - return performAction((inst) => ignorePackage(inst, packageId)); +export async function POST(_req: Request, ctx: { params: Promise<{ id: string }> }) { + const { id } = await ctx.params; + return performAction((inst) => ignorePackage(inst, id)); } diff --git a/e2e/api.spec.ts b/e2e/api.spec.ts index 673c789..fa90f28 100644 --- a/e2e/api.spec.ts +++ b/e2e/api.spec.ts @@ -26,8 +26,8 @@ test('GET /api/openapi serves the spec with all thirteen endpoints and the packa '/api/market/buy-plane', '/api/market/sell-plane', '/api/packages/{id}/abandon', + '/api/packages/{id}/ignore', // v0.29.0 (ISS-047 part B) '/api/packages/{id}/load', - '/api/packages/{packageId}/ignore', // v0.29.0 (ISS-047 part B) '/api/planes/{planeId}/fly', '/api/planes/{planeId}/queue', '/api/planes/{planeId}/queue/cancel', diff --git a/lib/openapi.test.ts b/lib/openapi.test.ts index 07d1b5a..cb9a7e7 100644 --- a/lib/openapi.test.ts +++ b/lib/openapi.test.ts @@ -9,8 +9,8 @@ describe('OpenAPI spec', () => { '/api/market/buy-plane', '/api/market/sell-plane', '/api/packages/{id}/abandon', + '/api/packages/{id}/ignore', // v0.29.0 (ISS-047 part B) '/api/packages/{id}/load', - '/api/packages/{packageId}/ignore', // v0.29.0 (ISS-047 part B) '/api/planes/{planeId}/fly', '/api/planes/{planeId}/queue', // v0.25.0 (ISS-043) '/api/planes/{planeId}/queue/cancel', // v0.25.0 (ISS-043) diff --git a/lib/openapi.ts b/lib/openapi.ts index 690de91..300a31b 100644 --- a/lib/openapi.ts +++ b/lib/openapi.ts @@ -165,14 +165,14 @@ export const openApiSpec = { }, }, }, - '/api/packages/{packageId}/ignore': { + '/api/packages/{id}/ignore': { post: { summary: 'Ignore an open job', description: 'Dismiss an OPEN job so it leaves the market permanently (v0.29.0, ISS-047 part B). No penalty and nothing else changes — the top-up logic spawns a replacement on the next tick. No body.', operationId: 'ignorePackage', parameters: [ - { name: 'packageId', in: 'path', required: true, schema: { type: 'string' } }, + { name: 'id', in: 'path', required: true, schema: { type: 'string' } }, ], responses: { 200: {