From 425c86405be28c94ac15cab6f8fec60586465c54 Mon Sep 17 00:00:00 2001 From: Benson Cho <100653148+choden-dev@users.noreply.github.com> Date: Fri, 17 Jul 2026 08:32:51 +1000 Subject: [PATCH 1/4] fix(orders): allow pickup slot change after order is PRINTED The select-timeslot endpoint previously rejected any order that was not PAID or AWAITING_PICKUP, so customers could not change their pickup slot once an admin marked the order PRINTED. Allow PRINTED orders to change their timeslot. The status is preserved (PRINTED -> PRINTED) so no invalid state transition occurs, and the previous timeslot's bookedCount is correctly decremented. --- app/api/shop/[orderId]/select-timeslot/route.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/app/api/shop/[orderId]/select-timeslot/route.ts b/app/api/shop/[orderId]/select-timeslot/route.ts index 1aef2da..01fa7cd 100644 --- a/app/api/shop/[orderId]/select-timeslot/route.ts +++ b/app/api/shop/[orderId]/select-timeslot/route.ts @@ -12,6 +12,8 @@ type RouteContext = { params: Promise<{ orderId: string }> }; * Customer selects (or changes) a pickup timeslot. * - PAID → AWAITING_PICKUP (first selection) * - AWAITING_PICKUP → AWAITING_PICKUP (re-selection / change) + * - PRINTED → PRINTED (change after the order has been printed; status is + * preserved so the admin still sees it as printed) * * Enforces capacity limits and includes pickup instructions in the response. * Triggers order confirmation email with pickup details and instructions. @@ -57,10 +59,14 @@ export async function POST(request: NextRequest, context: RouteContext) { return NextResponse.json({ error: "Order not found." }, { status: 404 }); } - if (order.status !== "PAID" && order.status !== "AWAITING_PICKUP") { + if ( + order.status !== "PAID" && + order.status !== "AWAITING_PICKUP" && + order.status !== "PRINTED" + ) { return NextResponse.json( { - error: `Cannot select timeslot for order in ${order.status} status. Order must be PAID or AWAITING_PICKUP.`, + error: `Cannot select timeslot for order in ${order.status} status. Order must be PAID, AWAITING_PICKUP, or PRINTED.`, }, { status: 400 }, ); @@ -93,7 +99,10 @@ export async function POST(request: NextRequest, context: RouteContext) { ); } - const isChangingTimeslot = order.status === "AWAITING_PICKUP"; + // An order is "changing" its timeslot (rather than selecting for the + // first time) whenever it already has one — i.e. any status past PAID. + const isChangingTimeslot = + order.status === "AWAITING_PICKUP" || order.status === "PRINTED"; const previousTimeslotId = isChangingTimeslot ? typeof order.pickupTimeslot === "object" && order.pickupTimeslot ? (order.pickupTimeslot as { id: string }).id From 0fd6a422236e5aed591a38f02775ecab6f9e8ad4 Mon Sep 17 00:00:00 2001 From: Benson Cho <100653148+choden-dev@users.noreply.github.com> Date: Fri, 17 Jul 2026 08:42:15 +1000 Subject: [PATCH 2/4] chore: pin Node 24 across all touchpoints Pin the Node runtime to v24 everywhere the version is declared: - .nvmrc: lts/* -> 24 - package.json engines.node: >=20 -> >=24 - Dockerfile: node:22-slim -> node:24-slim (builder + runner) - docker-entrypoint.sh / scripts/docker-build-step.sh: comment refs updated CI (actions/setup-node) reads node-version-file: .nvmrc, so it picks up 24 automatically. --- .nvmrc | 2 +- Dockerfile | 4 ++-- docker-entrypoint.sh | 2 +- package.json | 2 +- scripts/docker-build-step.sh | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.nvmrc b/.nvmrc index 1a2f5bd..a45fd52 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -lts/* \ No newline at end of file +24 diff --git a/Dockerfile b/Dockerfile index 70904ac..ec5a54a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,7 @@ # which is NOT the case for Cloudflare Workers Builds. Turbo's remote # cache, by contrast, IS preserved, so we get a true "skip pnpm install # when the lockfile hasn't changed" path on every deploy. -FROM node:22-slim AS builder +FROM node:24-slim AS builder WORKDIR /app @@ -140,7 +140,7 @@ ENV TURBO_TOKEN="" \ # ── Stage 3: Production runner ─────────────────────────────────────────── -FROM node:22-slim AS runner +FROM node:24-slim AS runner WORKDIR /app diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index b209ca7..be32995 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -135,7 +135,7 @@ if [ "$#" -gt 0 ]; then # # We read grep's output into the positional params via a NUL-safe loop # (`grep -rlZ` + `read -d ''`) so paths with spaces/newlines are handled - # correctly. GNU coreutils in the node:22-slim runtime supports `grep -rlZ` + # correctly. GNU coreutils in the node:24-slim runtime supports `grep -rlZ` # and `read -d`; local validation must use GNU tools (see the CMD note above). # # Best-effort throughout: any non-zero exit is captured into `swap_status` diff --git a/package.json b/package.json index 4fb54a2..8f6f331 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "wrangler": "^4.83.0" }, "engines": { - "node": ">=20", + "node": ">=24", "pnpm": "^10.0.0" }, "//cloudflare-workarounds": "jose is an explicit dep because payload uses it for JWT but pnpm doesn't hoist it. canvas is overridden with a local stub because pdfjs-dist optionally requires it (native addon) which breaks the build. See shims/canvas/index.js for details.", diff --git a/scripts/docker-build-step.sh b/scripts/docker-build-step.sh index 9c083fd..06586c4 100644 --- a/scripts/docker-build-step.sh +++ b/scripts/docker-build-step.sh @@ -18,7 +18,7 @@ # falling back to a plain `next build`. # # Kept as a standalone POSIX shell script (sh, not bash) so it runs cleanly -# inside node:22-slim without requiring extra packages. +# inside node:24-slim without requiring extra packages. set -eu From 02c8d9c6fa7f4e1c0d3cd29f38178abb5f406baf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Jul 2026 23:11:09 +0000 Subject: [PATCH 3/4] Retry Biome setup step in quality workflow --- .github/workflows/pull_request.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 8bf9e12..b685aae 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -18,7 +18,14 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - - name: Setup Biome + - name: Setup Biome (attempt 1) + id: setup_biome + continue-on-error: true + uses: biomejs/setup-biome@v2 + with: + version: 2.4.12 + - name: Setup Biome (retry on transient failure) + if: steps.setup_biome.outcome == 'failure' uses: biomejs/setup-biome@v2 with: version: 2.4.12 From f1c49a8f9c57a068524f86e0c022cdaa2591a67f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Jul 2026 23:22:13 +0000 Subject: [PATCH 4/4] ci: avoid setup-biome release API flake in quality job --- .github/workflows/pull_request.yml | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index b685aae..6359c7e 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -18,19 +18,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - - name: Setup Biome (attempt 1) - id: setup_biome - continue-on-error: true - uses: biomejs/setup-biome@v2 - with: - version: 2.4.12 - - name: Setup Biome (retry on transient failure) - if: steps.setup_biome.outcome == 'failure' - uses: biomejs/setup-biome@v2 - with: - version: 2.4.12 - name: Run Biome - run: biome ci . + run: npx --yes @biomejs/biome@2.4.12 ci . # Catches: # - lockfile drift (pnpm install --frozen-lockfile fails when