Skip to content

(SP: 3) [SHOP] complete cleanup across merchandising, admin operations, shipment visibility, and public runtime safety#439

Merged
ViktorSvertoka merged 7 commits intodevelopfrom
lso/feat/shop-legal
Apr 1, 2026
Merged

(SP: 3) [SHOP] complete cleanup across merchandising, admin operations, shipment visibility, and public runtime safety#439
ViktorSvertoka merged 7 commits intodevelopfrom
lso/feat/shop-legal

Conversation

@liudmylasovetovs
Copy link
Copy Markdown
Collaborator

@liudmylasovetovs liudmylasovetovs commented Mar 31, 2026

Description

This PR completes Phase 6 (P1 — completion and cleanup) for the Shop module after launch blockers were closed.

It finalizes remaining cleanup work across merchandising, admin product/order operations, guest shipment visibility, controlled post-order shipping edits, and public runtime/cache safety. The goal was to remove weak or decorative behavior, tighten operational correctness, expand audit/history visibility, and add explicit runtime/cache protection for public shop surfaces without widening scope beyond the planned cleanup items.


Related Issue

Issue: #<issue_number>


Changes

  • Completed merchandising cleanup by removing or finishing fake storefront options end-to-end within the planned Phase 6 scope
  • Completed remaining admin product/order cleanup: expanded audit/history coverage, improved admin completeness, added controlled guest shipment visibility and post-order shipping edit flow
  • Added explicit public runtime/cache intent and smoke coverage for /shop, /shop/products, /shop/products/[slug], and /shop/cart, plus a narrow follow-up to align the public cart env path with the readServerEnv() server env contract

Database Changes (if applicable)

  • Schema migration required
  • Seed data updated
  • Breaking changes to existing queries
  • Transaction-safe migration
  • Migration tested locally on Neon

How Has This Been Tested?

  • Tested locally
  • Verified in development environment
  • Checked responsive layout (if UI-related)
  • Tested accessibility (keyboard / screen reader)

Screenshots (if applicable)


Checklist

Before submitting

  • Code has been self-reviewed
  • No TypeScript or console errors
  • Code follows project conventions
  • Scope is limited to this feature/fix
  • No unrelated refactors included
  • English used in code, commits, and docs
  • New dependencies discussed with team
  • Database migration tested locally (if applicable)
  • GitHub Projects card moved to In Review

Reviewers

Summary by CodeRabbit

  • New Features

    • Admins can edit order shipping details from the order view.
    • Customers now see shipment status and tracking numbers; order history includes refund/cancel/edit actions.
  • Improvements

    • Order list converted to responsive card layout with clearer localized labels.
    • Product sorting now prioritizes featured items with deterministic tie-breakers.
    • Catalog/category logic cleaned up; storefront categories and "newest" canonicalization refined.
    • Shipping editor UI and translations added.
  • Tests

    • Extensive tests added for shipping edit flows, order/status display, catalog, and related UI.

@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Mar 31, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
devlovers-net Ignored Ignored Preview Mar 31, 2026 11:33pm

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 31, 2026

📝 Walkthrough

Walkthrough

Adds an admin-facing Nova Poshta shipping editor (client form + PATCH API + server service), threads shipmentStatus/trackingNumber through order models and UI, canonicalizes public catalog queries and storefront categories, extends admin audit logging for refund/cancel/edit flows, updates runtime/env reads, and adds extensive tests.

Changes

Cohort / File(s) Summary
Admin shipping editor (client + page integration)
frontend/app/[locale]/admin/shop/orders/[id]/ShippingEditForm.tsx, frontend/app/[locale]/admin/shop/orders/[id]/page.tsx
New client-side ShippingEditForm component and server-side integration on order detail page behind availability/CSRF checks.
Admin shipping API route
frontend/app/api/shop/admin/orders/[id]/shipping/route.ts
New PATCH handler with same-origin guard, admin auth, CSRF action admin:orders:shipping:edit, payload validation, delegates to service, maps service errors to HTTP responses.
Shipping edit service & types
frontend/lib/services/shop/shipping/admin-edit.ts, frontend/lib/validation/shop-admin-shipping.ts
Server-only service implementing transactional shipping edits, snapshot comparison, NP city/warehouse resolution, quote preservation logic, audit writes, and exported error/availability types.
Order models & summary plumbing
frontend/lib/services/orders/summary.ts, frontend/lib/services/orders/payment-intent.ts, frontend/lib/types/shop.ts
Adds shipmentStatus and trackingNumber to order types and threads them through parsing and service calls.
Guest shipment UI
frontend/lib/shop/guest-shipment-status.ts, frontend/app/[locale]/shop/checkout/success/MonobankRedirectStatus.tsx, frontend/app/[locale]/shop/checkout/success/page.tsx, frontend/app/[locale]/shop/orders/[id]/page.tsx
New formatter for guest-visible shipment statuses; checkout success and order detail pages render shipment status and tracking when present.
Admin audit logging additions
frontend/app/api/shop/admin/orders/[id]/refund/route.ts, frontend/app/api/shop/admin/orders/[id]/cancel-payment/route.ts
Routes now call writeAdminAudit after refund/cancel; audit failures are logged but do not change primary responses.
Catalog canonicalization & storefront categories
frontend/lib/shop/catalog-query.ts, frontend/lib/config/catalog.ts, frontend/lib/validation/shop.ts, frontend/app/[locale]/shop/products/page.tsx, frontend/app/api/shop/catalog/route.ts, frontend/components/shop/ProductFilters.tsx
Adds canonicalizePublicCatalogQuery to normalize legacy filter=new/category=new-arrivalssort=newest; introduces STOREFRONT_CATEGORIES and updates validation, routes, and UI filters.
Product query sorting & DB changes
frontend/db/queries/shop/products.ts
Sorting returns multiple ORDER expressions with tie-breakers and a featured sort; removed new-arrivals special-case.
Runtime/env & capability reads
frontend/lib/env/server-env.ts, frontend/lib/env/shop-legal.ts, frontend/app/[locale]/shop/cart/capabilities.ts, frontend/app/[locale]/shop/cart/page.tsx, frontend/app/[locale]/shop/page.tsx, frontend/app/[locale]/shop/products/[slug]/page.tsx, frontend/app/[locale]/shop/products/page.tsx
Adds fallback env keys and replaces direct process.env reads with readServerEnv; adds runtime: 'nodejs' and dynamic: 'force-dynamic' exports to several public routes.
Homepage & catalog plumbing
frontend/lib/shop/data.ts, frontend/lib/shop/catalog-query.ts
Simplified homepage new-arrivals to use newest catalog slice; added catalog query canonicalization.
Validation & schema updates
frontend/lib/validation/shop.ts, frontend/lib/validation/shop-admin-shipping.ts
Switches category enum to storefront values; re-exports checkout shipping schema as admin edit schema and adds inferred input type.
I18n additions
frontend/messages/en.json, frontend/messages/uk.json, frontend/messages/pl.json
New message keys for shipping editor UI and errors, order card fields, payment/shipment status labels, and admin history actions (refund, cancelPayment, editShipping).
Tests (new/updated)
frontend/lib/tests/... (many files)
Extensive tests added/updated covering PATCH shipping route/service, admin audit logging, catalog canonicalization/sorting, shipment UI, env contract, and runtime config exports. See individual test files for details.

Sequence Diagram

sequenceDiagram
    participant Admin as Admin (Browser)
    participant Form as ShippingEditForm (Client)
    participant API as PATCH /api/.../shipping (Server)
    participant Service as applyAdminOrderShippingEdit (Service)
    participant DB as Database (orders, orderShipping, npCities/npWarehouses)
    participant Audit as AdminAudit (writeAdminAudit)

    Admin->>Form: Open editor & submit shipping changes
    Form->>Form: Validate inputs & block duplicate submits
    Form->>API: PATCH JSON + x-csrf-token (same-origin, credentials)
    API->>API: Enforce same-origin, require admin auth, validate CSRF, validate payload
    API->>Service: invoke applyAdminOrderShippingEdit(orderId, shipping, actorUserId, requestId)
    Service->>DB: Lock order row, load current shipping snapshot
    Service->>DB: Resolve city/warehouse data and compare snapshots
    alt snapshots identical
        Service-->>API: return { changed: false, order: { id, shippingMethodCode } }
    else snapshots differ
        Service->>DB: update orders row, upsert orderShipping
        Service->>Audit: writeAdminAudit(edit_shipping, payload, dedupeSeed)
        Service-->>API: return { changed: true, order: { id, shippingMethodCode } }
    end
    API-->>Form: HTTP 200 JSON (success/changed)
    Form->>Form: clear submission state
    Form->>Admin: trigger router.refresh() in startTransition
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

refactor

Suggested reviewers

  • AM1007
  • LesiaUKR
  • ViktorSvertoka

Poem

🐰 Hopped in code with tidy paws,

I nudged some shipping, fixed the laws;
Queries trimmed and labels neat,
Audits logged each admin feat;
Fresh deploy — the rabbit hops, complete!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: completing a comprehensive cleanup phase for the Shop module covering merchandising, admin operations, shipment visibility, and public runtime safety.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch lso/feat/shop-legal

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7b40d4a62e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread frontend/lib/shop/catalog-query.ts
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (6)
frontend/lib/services/shop/shipping/admin-edit.ts (1)

143-148: JSON.stringify comparison is order-dependent.

snapshotsEqual relies on JSON.stringify for deep equality. This works correctly here because buildNextComparable and toComparableSnapshot construct objects with consistent property order. However, this is fragile if the structure changes.

Consider using a deep-equal utility if this comparison becomes more complex in the future.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/lib/services/shop/shipping/admin-edit.ts` around lines 143 - 148,
snapshotsEqual uses JSON.stringify which is order-dependent and fragile; replace
the stringify comparison in snapshotsEqual with a proper deep-equality check
(e.g., import and call a known deep-equal utility such as lodash/isEqual or
fast-deep-equal) and ensure it handles the nullable left parameter (call
deepEqual(left, right) or treat null appropriately). Update the imports and
change the function body in snapshotsEqual to use the chosen deep-equal function
instead of JSON.stringify to make comparisons robust against property order
changes.
frontend/app/[locale]/admin/shop/orders/[id]/ShippingEditForm.tsx (2)

113-154: Consider adding client-side required field validation.

The form submits directly without checking if required fields (cityRef, recipientFullName, recipientPhone) are populated. While the server validates the payload, providing immediate client-side feedback improves UX:

  1. Add required attribute to mandatory inputs, or
  2. Check values before fetch and set a local error.

This prevents unnecessary network round-trips for obviously invalid submissions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/app/`[locale]/admin/shop/orders/[id]/ShippingEditForm.tsx around
lines 113 - 154, The onSubmit handler sends the PATCH without validating
required fields, so add client-side validation in onSubmit before calling fetch:
check cityRef, recipientFullName, and recipientPhone (and warehouseRef when
isWarehouseMethod is true) and if any are missing call setError(...) with a
user-friendly message and setIsSubmitting(false) and return; alternatively add
HTML required attributes on the corresponding inputs to enforce browser
validation, but ensure onSubmit still guards against empty values by validating
and returning early to avoid the network request (update the onSubmit function
and any related UI input elements that bind cityRef, recipientFullName,
recipientPhone, warehouseRef).

342-350: Add aria-describedby to link submit button to error message.

The error message uses role="alert" which announces changes, but linking it to the submit button via aria-describedby would provide better context for screen reader users when they focus the button after an error:

♿ Proposed accessibility improvement
         <button
           type="submit"
           disabled={isSubmitting || isPending}
           aria-busy={isSubmitting || isPending}
+          aria-describedby={error ? errorId : undefined}
           className="rounded-lg border border-emerald-500/30 bg-emerald-500/5 px-3 py-2 text-sm font-medium text-emerald-700 transition-colors hover:bg-emerald-500/10 disabled:cursor-not-allowed disabled:opacity-50 dark:text-emerald-100"
         >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/app/`[locale]/admin/shop/orders/[id]/ShippingEditForm.tsx around
lines 342 - 350, The submit button currently shown in the <button ...> markup
(controlled by isSubmitting and isPending and rendering
tEditor('save')/tEditor('saving')) should include an aria-describedby that
points to the error message element so screen readers can associate the button
with the alert; add a stable id (e.g., shippingErrorId or similar) to the
element that renders the error message (the element with role="alert") and
conditionally add aria-describedby={errorId} to the submit button when an error
exists so the button references that role="alert" node for context.
frontend/app/api/shop/admin/orders/[id]/shipping/route.ts (1)

282-290: Consider extracting duplicated actor ID extraction logic.

The pattern for extracting actorUserId (lines 285-288) is duplicated from the POST handler (lines 127-130):

actorUserId:
  typeof adminUser?.id === 'string' && adminUser.id.trim().length > 0
    ? adminUser.id
    : null,

Consider extracting this to a small helper function to reduce duplication and ensure consistent behavior:

function extractActorUserId(adminUser: { id?: string } | null): string | null {
  return typeof adminUser?.id === 'string' && adminUser.id.trim().length > 0
    ? adminUser.id
    : null;
}

This is a minor DRY improvement and not blocking.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/app/api/shop/admin/orders/`[id]/shipping/route.ts around lines 282 -
290, The duplicated actor ID extraction logic used when calling
applyAdminOrderShippingEdit and in the POST handler should be moved into a small
helper to avoid repetition and ensure consistent behavior; add a function like
extractActorUserId(adminUser: { id?: string } | null): string | null that
returns adminUser.id when typeof adminUser?.id === 'string' and
adminUser.id.trim().length > 0, otherwise null, then replace the inline ternary
in both callers (the place constructing the actorUserId for
applyAdminOrderShippingEdit and the POST handler) with a call to
extractActorUserId(adminUser).
frontend/lib/tests/shop/admin-shipping-edit.test.ts (1)

47-67: Consider extracting test fixture factory or using a test helper module.

The seedEditableOrder function uses as any assertions (lines 55, 67, 85, 116, 128) to bypass TypeScript when inserting test data. While this works for tests, it means the test won't catch schema drift.

If the schema changes (e.g., a required field is added), these tests will pass locally but the actual service may fail. Consider:

  1. Using a shared test factory that mirrors the actual insertion logic, or
  2. Importing and using the same insert helpers used by the production code.

This is a trade-off between test isolation and catching schema mismatches early.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/lib/tests/shop/admin-shipping-edit.test.ts` around lines 47 - 67,
The test uses unsafe "as any" casts when inserting fixtures (see
seedEditableOrder and the npCities/npWarehouses inserts) which hides schema
drift; replace these casts by creating properly typed fixture objects or a
shared test factory that returns the correct types (or import and reuse the
production insert helpers) and use those typed objects in db.insert calls;
update all occurrences of "as any" referenced in seedEditableOrder (lines near
the npCities/npWarehouses inserts and the other spots called out in the review)
so the test will fail when required schema fields change.
frontend/messages/pl.json (1)

896-914: Unicode escapes are inconsistent with the rest of the file.

The shippingEditor section (lines 896-914) uses Unicode escapes like \u00f3 (ó), \u0142 (ł), \u0119 (ę), etc., while the rest of the Polish translations use UTF-8 characters directly. This works correctly but creates inconsistency that makes the file harder to maintain.

Consider normalizing to UTF-8 characters for consistency:

-          "subtitle": "Zapisuj tylko zweryfikowane referencje Nova Poshta i dane odbiorcy dla tego zam\u00f3wienia.",
+          "subtitle": "Zapisuj tylko zweryfikowane referencje Nova Poshta i dane odbiorcy dla tego zamówienia.",

This is a minor maintainability concern and not blocking.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/messages/pl.json` around lines 896 - 914, The shippingEditor block
contains escaped Unicode sequences (e.g., "\u00f3", "\u0142", "\u0119") which
are inconsistent with the rest of the file; replace those escapes in the
"shippingEditor" object (keys like heading, subtitle, addressLine1,
addressLine2, currentCity, currentPickupPoint, save, saving and each entry under
errors: network, security, invalid, notAllowed, adminDisabled, generic) with
their direct UTF-8 Polish characters (ó, ł, ę, ś, etc.) and save the JSON file
as UTF-8 to maintain consistent encoding.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/app/api/shop/admin/orders/`[id]/cancel-payment/route.ts:
- Around line 110-135: The audit write using writeAdminAudit (with
orderIdForLog, requestId and payload built from result) must be guarded so
failures don't turn a successful cancel into a 5xx; wrap the writeAdminAudit
invocation in a try/catch (or fire-and-forget Promise.catch) and on error log
the failure with contextual identifiers (orderIdForLog, requestId, action:
'cancel_payment') but do not rethrow or change the response flow so the cancel
result is returned to the client even if the audit fails.

In `@frontend/app/api/shop/admin/orders/`[id]/refund/route.ts:
- Around line 166-187: The current awaited call to writeAdminAudit can cause the
whole refund endpoint to fail if audit persistence errors; change this so
refundOrder's success isn't blocked by audit logging: after calling refundOrder
(the successful path that uses orderSummary and orderIdForLog), invoke
writeAdminAudit in a non-fatal way—either fire-and-forget (do not await) or wrap
the await in a try/catch that catches/logs any error (use requestId,
orderIdForLog, adminUser context) and does not rethrow; ensure the catch logs
the failure and moves on so the endpoint returns the successful refund response
even if writeAdminAudit fails.

---

Nitpick comments:
In `@frontend/app/`[locale]/admin/shop/orders/[id]/ShippingEditForm.tsx:
- Around line 113-154: The onSubmit handler sends the PATCH without validating
required fields, so add client-side validation in onSubmit before calling fetch:
check cityRef, recipientFullName, and recipientPhone (and warehouseRef when
isWarehouseMethod is true) and if any are missing call setError(...) with a
user-friendly message and setIsSubmitting(false) and return; alternatively add
HTML required attributes on the corresponding inputs to enforce browser
validation, but ensure onSubmit still guards against empty values by validating
and returning early to avoid the network request (update the onSubmit function
and any related UI input elements that bind cityRef, recipientFullName,
recipientPhone, warehouseRef).
- Around line 342-350: The submit button currently shown in the <button ...>
markup (controlled by isSubmitting and isPending and rendering
tEditor('save')/tEditor('saving')) should include an aria-describedby that
points to the error message element so screen readers can associate the button
with the alert; add a stable id (e.g., shippingErrorId or similar) to the
element that renders the error message (the element with role="alert") and
conditionally add aria-describedby={errorId} to the submit button when an error
exists so the button references that role="alert" node for context.

In `@frontend/app/api/shop/admin/orders/`[id]/shipping/route.ts:
- Around line 282-290: The duplicated actor ID extraction logic used when
calling applyAdminOrderShippingEdit and in the POST handler should be moved into
a small helper to avoid repetition and ensure consistent behavior; add a
function like extractActorUserId(adminUser: { id?: string } | null): string |
null that returns adminUser.id when typeof adminUser?.id === 'string' and
adminUser.id.trim().length > 0, otherwise null, then replace the inline ternary
in both callers (the place constructing the actorUserId for
applyAdminOrderShippingEdit and the POST handler) with a call to
extractActorUserId(adminUser).

In `@frontend/lib/services/shop/shipping/admin-edit.ts`:
- Around line 143-148: snapshotsEqual uses JSON.stringify which is
order-dependent and fragile; replace the stringify comparison in snapshotsEqual
with a proper deep-equality check (e.g., import and call a known deep-equal
utility such as lodash/isEqual or fast-deep-equal) and ensure it handles the
nullable left parameter (call deepEqual(left, right) or treat null
appropriately). Update the imports and change the function body in
snapshotsEqual to use the chosen deep-equal function instead of JSON.stringify
to make comparisons robust against property order changes.

In `@frontend/lib/tests/shop/admin-shipping-edit.test.ts`:
- Around line 47-67: The test uses unsafe "as any" casts when inserting fixtures
(see seedEditableOrder and the npCities/npWarehouses inserts) which hides schema
drift; replace these casts by creating properly typed fixture objects or a
shared test factory that returns the correct types (or import and reuse the
production insert helpers) and use those typed objects in db.insert calls;
update all occurrences of "as any" referenced in seedEditableOrder (lines near
the npCities/npWarehouses inserts and the other spots called out in the review)
so the test will fail when required schema fields change.

In `@frontend/messages/pl.json`:
- Around line 896-914: The shippingEditor block contains escaped Unicode
sequences (e.g., "\u00f3", "\u0142", "\u0119") which are inconsistent with the
rest of the file; replace those escapes in the "shippingEditor" object (keys
like heading, subtitle, addressLine1, addressLine2, currentCity,
currentPickupPoint, save, saving and each entry under errors: network, security,
invalid, notAllowed, adminDisabled, generic) with their direct UTF-8 Polish
characters (ó, ł, ę, ś, etc.) and save the JSON file as UTF-8 to maintain
consistent encoding.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0f070133-f8b6-4e0c-bf21-251bb5cb408b

📥 Commits

Reviewing files that changed from the base of the PR and between 6f74dda and 7b40d4a.

📒 Files selected for processing (47)
  • frontend/app/[locale]/admin/shop/orders/[id]/ShippingEditForm.tsx
  • frontend/app/[locale]/admin/shop/orders/[id]/page.tsx
  • frontend/app/[locale]/shop/cart/capabilities.ts
  • frontend/app/[locale]/shop/cart/page.tsx
  • frontend/app/[locale]/shop/checkout/success/MonobankRedirectStatus.tsx
  • frontend/app/[locale]/shop/checkout/success/page.tsx
  • frontend/app/[locale]/shop/orders/[id]/page.tsx
  • frontend/app/[locale]/shop/orders/page.tsx
  • frontend/app/[locale]/shop/page.tsx
  • frontend/app/[locale]/shop/products/[slug]/page.tsx
  • frontend/app/[locale]/shop/products/page.tsx
  • frontend/app/api/shop/admin/orders/[id]/cancel-payment/route.ts
  • frontend/app/api/shop/admin/orders/[id]/refund/route.ts
  • frontend/app/api/shop/admin/orders/[id]/shipping/route.ts
  • frontend/app/api/shop/catalog/route.ts
  • frontend/components/shop/ProductFilters.tsx
  • frontend/db/queries/shop/products.ts
  • frontend/lib/config/catalog.ts
  • frontend/lib/env/server-env.ts
  • frontend/lib/env/shop-legal.ts
  • frontend/lib/services/orders/payment-intent.ts
  • frontend/lib/services/orders/summary.ts
  • frontend/lib/services/shop/shipping/admin-edit.ts
  • frontend/lib/shop/catalog-query.ts
  • frontend/lib/shop/data.ts
  • frontend/lib/shop/guest-shipment-status.ts
  • frontend/lib/tests/shop/admin-csrf-contract.test.ts
  • frontend/lib/tests/shop/admin-order-detail-customer-summary.test.ts
  • frontend/lib/tests/shop/admin-order-detail-history.test.ts
  • frontend/lib/tests/shop/admin-order-detail-operational-actions.test.ts
  • frontend/lib/tests/shop/admin-order-payment-audit-routes.test.ts
  • frontend/lib/tests/shop/admin-order-timeline-query.test.ts
  • frontend/lib/tests/shop/admin-shipping-edit-route.test.ts
  • frontend/lib/tests/shop/admin-shipping-edit.test.ts
  • frontend/lib/tests/shop/catalog-merchandising-cleanup.test.ts
  • frontend/lib/tests/shop/catalog-query-sorting.test.ts
  • frontend/lib/tests/shop/checkout-success-monobank-shipment.test.ts
  • frontend/lib/tests/shop/checkout-success-page-access.test.ts
  • frontend/lib/tests/shop/order-status-token.test.ts
  • frontend/lib/tests/shop/public-cart-env-contract.test.ts
  • frontend/lib/tests/shop/public-shop-runtime-cache-smoke.test.ts
  • frontend/lib/types/shop.ts
  • frontend/lib/validation/shop-admin-shipping.ts
  • frontend/lib/validation/shop.ts
  • frontend/messages/en.json
  • frontend/messages/pl.json
  • frontend/messages/uk.json

Comment thread frontend/app/api/shop/admin/orders/[id]/cancel-payment/route.ts Outdated
Comment thread frontend/app/api/shop/admin/orders/[id]/refund/route.ts Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
frontend/app/[locale]/admin/shop/orders/[id]/ShippingEditForm.tsx (2)

146-165: Submit trimmed values to maintain data consistency.

Validation uses trimmed values (lines 119-122), but the request body uses the original untrimmed values. This could store data with leading/trailing whitespace, causing display inconsistencies or lookup issues.

Proposed fix to use trimmed values in request body
         body: JSON.stringify({
           provider: 'nova_poshta',
           methodCode,
           selection: {
-            cityRef,
+            cityRef: trimmedCityRef,
             ...(isWarehouseMethod
-              ? { warehouseRef }
-              : { addressLine1, addressLine2 }),
+              ? { warehouseRef: trimmedWarehouseRef }
+              : { addressLine1: addressLine1.trim(), addressLine2: addressLine2.trim() }),
           },
           recipient: {
-            fullName: recipientFullName,
-            phone: recipientPhone,
+            fullName: trimmedRecipientFullName,
+            phone: trimmedRecipientPhone,
             ...(recipientEmail.trim().length > 0
               ? { email: recipientEmail }
               : {}),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/app/`[locale]/admin/shop/orders/[id]/ShippingEditForm.tsx around
lines 146 - 165, The request body is sending untrimmed form fields while
validation used trimmed variants; update ShippingEditForm.tsx so the body uses
the trimmed values for recipientEmail, recipientComment, recipientFullName,
recipientPhone and addressLine1/addressLine2/warehouseRef where applicable
(reuse the existing trimmed variables or create trimmed counterparts) instead of
the raw state values referenced by methodCode/isWarehouseMethod so submitted
data has leading/trailing whitespace removed.

274-279: Consider adding conditional required attribute for courier address.

For consistency with the server-side validation that requires addressLine1 for courier delivery, this input should have the required attribute when the courier method is selected. This provides browser-native validation feedback.

Proposed fix
             <input
               id="shipping-address-line-1"
               value={addressLine1}
               onChange={event => setAddressLine1(event.target.value)}
+              required
               className="border-border bg-background text-foreground w-full rounded-lg border px-3 py-2 text-sm"
             />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/app/`[locale]/admin/shop/orders/[id]/ShippingEditForm.tsx around
lines 274 - 279, The shipping address input for addressLine1 lacks a conditional
required attribute; add browser-side validation by computing a boolean (e.g.,
isCourier = deliveryMethod === 'courier' or shippingMethod === 'COURIER' based
on your component state) and set required={isCourier} on the <input
id="shipping-address-line-1"> that uses value={addressLine1} and onChange={event
=> setAddressLine1(event.target.value)} so the field is required only when
courier delivery is selected to match server-side validation.
frontend/lib/tests/shop/admin-shipping-edit-form.test.ts (1)

72-110: LGTM, but consider adding a test for courier addressLine1 requirement.

This test correctly verifies warehouseRef is required for warehouse methods. Once the client-side validation is updated to require addressLine1 for courier delivery (as noted in the form review), consider adding a parallel test case.

Example test to add after fixing form validation
it('requires addressLine1 locally for courier methods before sending PATCH', async () => {
  const fetchMock = vi.fn();
  vi.stubGlobal('fetch', fetchMock);

  const { ShippingEditForm } =
    await import('@/app/[locale]/admin/shop/orders/[id]/ShippingEditForm');

  const { container } = render(
    createElement(ShippingEditForm, {
      orderId: '550e8400-e29b-41d4-a716-446655440102',
      csrfToken: 'csrf-token',
      initialShipping: {
        methodCode: 'NP_COURIER',
        cityRef: 'city-ref',
        cityLabel: null,
        warehouseRef: null,
        warehouseLabel: null,
        addressLine1: '',
        addressLine2: null,
        recipientFullName: 'Test User',
        recipientPhone: '+380501112233',
        recipientEmail: null,
        recipientComment: null,
      },
    })
  );

  const form = container.querySelector('form');
  if (!form) throw new Error('ShippingEditForm form not rendered');

  fireEvent.submit(form);

  await waitFor(() => {
    expect(screen.getByRole('alert')).toHaveTextContent(
      'shop.orders.detail.shippingEditor.errors.invalid'
    );
  });
  expect(fetchMock).not.toHaveBeenCalled();
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/lib/tests/shop/admin-shipping-edit-form.test.ts` around lines 72 -
110, Add a new test mirroring the existing warehouse test to assert client-side
validation requires addressLine1 for courier deliveries: create a test in
admin-shipping-edit-form.test.ts that imports ShippingEditForm, stubs fetch,
renders ShippingEditForm with methodCode 'NP_COURIER' and addressLine1 set to an
empty string, submits the form, waits for the same alert text
'shop.orders.detail.shippingEditor.errors.invalid', and asserts fetch was not
called; this will ensure the form validation (ShippingEditForm) enforces
addressLine1 before sending the PATCH.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/app/`[locale]/admin/shop/orders/[id]/ShippingEditForm.tsx:
- Around line 124-133: Client validation misses requiring addressLine1 for
courier deliveries: update the hasRequiredFields check in ShippingEditForm.tsx
to also require trimmedAddressLine1 when the delivery method is courier (i.e.
when isWarehouseMethod is false / method === 'NP_COURIER'). Modify the boolean
expression that builds hasRequiredFields (alongside trimmedCityRef,
trimmedRecipientFullName, trimmedRecipientPhone, trimmedWarehouseRef) to include
trimmedAddressLine1.length > 0 for non-warehouse/courier flows, and ensure
setError(tEditor('errors.invalid')) remains used when the check fails.

---

Nitpick comments:
In `@frontend/app/`[locale]/admin/shop/orders/[id]/ShippingEditForm.tsx:
- Around line 146-165: The request body is sending untrimmed form fields while
validation used trimmed variants; update ShippingEditForm.tsx so the body uses
the trimmed values for recipientEmail, recipientComment, recipientFullName,
recipientPhone and addressLine1/addressLine2/warehouseRef where applicable
(reuse the existing trimmed variables or create trimmed counterparts) instead of
the raw state values referenced by methodCode/isWarehouseMethod so submitted
data has leading/trailing whitespace removed.
- Around line 274-279: The shipping address input for addressLine1 lacks a
conditional required attribute; add browser-side validation by computing a
boolean (e.g., isCourier = deliveryMethod === 'courier' or shippingMethod ===
'COURIER' based on your component state) and set required={isCourier} on the
<input id="shipping-address-line-1"> that uses value={addressLine1} and
onChange={event => setAddressLine1(event.target.value)} so the field is required
only when courier delivery is selected to match server-side validation.

In `@frontend/lib/tests/shop/admin-shipping-edit-form.test.ts`:
- Around line 72-110: Add a new test mirroring the existing warehouse test to
assert client-side validation requires addressLine1 for courier deliveries:
create a test in admin-shipping-edit-form.test.ts that imports ShippingEditForm,
stubs fetch, renders ShippingEditForm with methodCode 'NP_COURIER' and
addressLine1 set to an empty string, submits the form, waits for the same alert
text 'shop.orders.detail.shippingEditor.errors.invalid', and asserts fetch was
not called; this will ensure the form validation (ShippingEditForm) enforces
addressLine1 before sending the PATCH.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 78a8792a-64e6-422c-8001-d1fabac99dec

📥 Commits

Reviewing files that changed from the base of the PR and between 7b40d4a and 223a11a.

📒 Files selected for processing (5)
  • frontend/app/[locale]/admin/shop/orders/[id]/ShippingEditForm.tsx
  • frontend/app/api/shop/admin/orders/[id]/cancel-payment/route.ts
  • frontend/app/api/shop/admin/orders/[id]/refund/route.ts
  • frontend/lib/tests/shop/admin-order-payment-audit-routes.test.ts
  • frontend/lib/tests/shop/admin-shipping-edit-form.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/lib/tests/shop/admin-order-payment-audit-routes.test.ts

Comment thread frontend/app/[locale]/admin/shop/orders/[id]/ShippingEditForm.tsx
…ress requirements and trimmed payload submission
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
frontend/app/[locale]/admin/shop/orders/[id]/ShippingEditForm.tsx (1)

129-139: Previous concern addressed; minor enhancement opportunity remains.

The addressLine1 requirement for courier delivery has been correctly added (line 134). However, client-side validation is less strict than server-side: the server enforces fullName.min(2), addressLine1.min(3), phone regex (/^(?:\+380\d{9}|0\d{9})$/), and email format, while the client only checks for non-empty values. Users could pass client validation but receive server-side errors.

Consider aligning client validation with server constraints for a smoother UX, or accept that server validation provides the definitive check.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/app/`[locale]/admin/shop/orders/[id]/ShippingEditForm.tsx around
lines 129 - 139, The client-side validation in ShippingEditForm.tsx (the
hasRequiredFields check and subsequent setError/tEditor usage) is weaker than
server rules; update the validation logic around the hasRequiredFields
calculation to mirror server constraints: require
trimmedRecipientFullName.length >= 2, require trimmedAddressLine1.length >= 3
when isCourierMethod, validate trimmedRecipientPhone against the server regex
/^(?:\+380\d{9}|0\d{9})$/, and validate trimmedRecipientEmail using a basic
email format check before allowing submit; keep trimming and existing
warehouse/courier conditional checks and use setError(tEditor('errors.invalid'))
if any of these stricter checks fail.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@frontend/app/`[locale]/admin/shop/orders/[id]/ShippingEditForm.tsx:
- Around line 129-139: The client-side validation in ShippingEditForm.tsx (the
hasRequiredFields check and subsequent setError/tEditor usage) is weaker than
server rules; update the validation logic around the hasRequiredFields
calculation to mirror server constraints: require
trimmedRecipientFullName.length >= 2, require trimmedAddressLine1.length >= 3
when isCourierMethod, validate trimmedRecipientPhone against the server regex
/^(?:\+380\d{9}|0\d{9})$/, and validate trimmedRecipientEmail using a basic
email format check before allowing submit; keep trimming and existing
warehouse/courier conditional checks and use setError(tEditor('errors.invalid'))
if any of these stricter checks fail.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2e64839a-cca0-48ef-9f6e-33b41ec895f8

📥 Commits

Reviewing files that changed from the base of the PR and between 223a11a and eb45ed3.

📒 Files selected for processing (2)
  • frontend/app/[locale]/admin/shop/orders/[id]/ShippingEditForm.tsx
  • frontend/lib/tests/shop/admin-shipping-edit-form.test.ts
✅ Files skipped from review due to trivial changes (1)
  • frontend/lib/tests/shop/admin-shipping-edit-form.test.ts

@ViktorSvertoka ViktorSvertoka merged commit 2049f86 into develop Apr 1, 2026
7 checks passed
@ViktorSvertoka ViktorSvertoka deleted the lso/feat/shop-legal branch April 1, 2026 04:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants