Skip to content

checkout, address, admin and orders screen UI completed#7

Merged
Ayushman8 merged 1 commit into
mainfrom
development
Jun 25, 2026
Merged

checkout, address, admin and orders screen UI completed#7
Ayushman8 merged 1 commit into
mainfrom
development

Conversation

@Ayushman8

@Ayushman8 Ayushman8 commented Jun 25, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features
    • Added shopping and order screens for viewing orders, order details, checkout, and shipping addresses.
    • Added admin sections for dashboards, orders, products, and product add/edit flows.
    • Introduced product image picking and address selection/editing in the app.
  • Bug Fixes
    • Improved loading states, empty states, and refresh behavior across key screens.
    • Added validation and feedback for checkout and product forms.

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds customer address, checkout, and order history/detail screens, plus an admin area with dashboard, orders, and product management routes. It also adds product image-picker support to the app.

Changes

Customer order flow

Layer / File(s) Summary
Address management screen
client/app/addresses/index.tsx
Loads dummy addresses, opens add/edit modal state, and renders address cards with default, edit, delete, and save controls.
Checkout flow
client/app/checkout.tsx
Loads a selected shipping address, tracks payment selection, validates placement, and renders the address, payment, and order summary sections.
Orders list
client/app/orders/index.tsx
Loads dummy orders and renders tappable order cards with badges, image previews, and totals.
Order details view
client/app/orders/[id].tsx
Loads one order by id and renders the status timeline, products, shipping details, and payment summary.

Admin management flow

Layer / File(s) Summary
Admin shell and dashboard
client/app/admin/_layout.tsx, client/app/admin/index.tsx
Adds an admin-only tab layout and a dashboard that loads stats and recent orders.
Admin orders
client/app/admin/orders.tsx
Loads dummy admin orders, supports refresh, and opens a status-update modal that mutates local order state.
Products route shell and list
client/app/admin/products/_layout.tsx, client/app/admin/products/index.tsx
Adds the products stack layout and a list view with add, edit, delete, and refresh actions.
Product create form
client/app/admin/products/add.tsx, client/package.json
Adds the product creation form with category, size, image, featured, and validation controls, plus image-picker support.
Product edit form
client/app/admin/products/edit/[id].tsx
Adds the edit screen that loads a product by id, manages existing and new images, and submits multipart updates.

Sequence Diagram(s)

Checkout and order navigation

sequenceDiagram
  participant Checkout
  participant dummyAddress
  participant Router
  participant Orders
  Checkout->>dummyAddress: load default shipping address
  dummyAddress-->>Checkout: selectedAddress
  Checkout->>Router: navigate to /orders after place order
  Router->>Orders: mount orders list
Loading

Admin product image flow

sequenceDiagram
  participant AdminProducts
  participant Router
  participant AddProduct
  participant EditProduct
  participant ImagePicker
  AdminProducts->>Router: open add/edit route
  Router->>AddProduct: mount /admin/products/add
  Router->>EditProduct: mount /admin/products/edit/:id
  AddProduct->>ImagePicker: launch image chooser
  EditProduct->>ImagePicker: launch image chooser
  ImagePicker-->>AddProduct: selected URIs
  ImagePicker-->>EditProduct: selected URIs
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 I hopped from cart to checkout light,
Then bounced through dashboards, neat and bright.
With orders, addresses, and product trees,
My whiskers twitched in UI breeze.
Hooray—this burrow feels just right!

🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main scope of the PR by summarizing the new checkout, address, admin, and orders UI screens.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 development

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🧹 Nitpick comments (3)
client/app/admin/orders.tsx (1)

42-47: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

updating is never set to true, making the modal spinner branch dead code.

updateStatus only calls setUpdating(false), and no path sets it true, so the loading state at lines 141-145 never renders. When this is wired to a real async status update, set setUpdating(true) before the call (and reset in a finally).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/app/admin/orders.tsx` around lines 42 - 47, The `updateStatus` flow in
`orders.tsx` never sets the `updating` state to true, so the spinner branch in
the modal cannot be reached. Update the `updateStatus` handler to mark
`setUpdating(true)` before starting the async status change, and ensure it is
always cleared afterward (preferably in a `finally` block) around the real
update logic so the modal loading UI reflects the request state.
client/app/admin/products/add.tsx (2)

40-49: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

submitting is never set, and handleSubmit has no submit path.

handleSubmit only runs validation and returns; it never sets submitting/persists the product, so the disabled/spinner UI on the submit button (Lines 208-221) is dead. This appears unfinished compared to the edit screen's handleSubmit.

Want me to scaffold the create-submission flow (FormData + image upload) mirroring edit/[id].tsx?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/app/admin/products/add.tsx` around lines 40 - 49, The create-product
submit flow in handleSubmit is incomplete: it only validates fields and returns,
so submitting is never set and no product is persisted. Update handleSubmit in
add.tsx to mirror the edit screen’s submit path by setting submitting true,
building the payload/FormData, handling image upload and create request, then
clearing submitting in finally so the button spinner/disabled state in the
submit UI is actually used.

26-31: 📐 Maintainability & Code Quality | 🔵 Trivial

Replace deprecated MediaTypeOptions with string array in mediaTypes.

ImagePicker.MediaTypeOptions is deprecated in expo-image-picker v15+. Replace MediaTypeOptions.Images with the string array ['images'].

mediaTypes: ['images'],
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/app/admin/products/add.tsx` around lines 26 - 31, Update the
ImagePicker usage in add.tsx to remove the deprecated
ImagePicker.MediaTypeOptions.Images value and use the new string-array form for
mediaTypes instead; locate the call to ImagePicker.launchImageLibraryAsync and
change the mediaTypes option to the equivalent ['images'] value while keeping
the rest of the selection options unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@client/app/addresses/index.tsx`:
- Around line 33-35: The address mock data in fetchAddresses is bypassing the
shared type contract with an any cast, which lets invalid values like "Office"
slip into UI state. Remove the as any cast and either normalize the dummyAddress
source values to the existing Address type or update the shared union to include
the correct label, so the data passed into setAddresses matches the contract
used by the label-selection flow.
- Around line 57-59: The delete flow is currently a no-op because
handleDeleteAddress is empty even though the UI calls it from the delete button.
Update handleDeleteAddress in the addresses index component to perform the
actual delete action (and any needed state refresh or API call), and make sure
the button’s onClick remains wired to that real handler so deleting an address
works end to end.
- Around line 51-55: The handleSaveAddress flow only closes the modal, resets
the form, and refetches addresses, so it never persists add/edit changes. Update
handleSaveAddress in the addresses screen to call the actual create/update logic
using the current form values before closing, and only reset/refetch after the
save succeeds. Use the existing handleSaveAddress, resetForm, and fetchAddresses
flow to wire in the missing persistence path.

In `@client/app/admin/orders.tsx`:
- Line 79: The conditional render in orders.tsx is contradictory because it only
runs when order.user is falsy, but then reads order.user?._id and always falls
back to N/A; update the JSX in the order row to either remove this unreachable
ID line or change the condition so the ID is shown when the user exists but is
missing a name, and keep the logic aligned with fetchOrders/dummyUser so the
display matches the actual data shape.

In `@client/app/admin/products/edit/`[id].tsx:
- Around line 33-48: The product load flow in the edit page currently assumes
dummyProducts.find always returns a product, so an unknown id falls into the
catch with a misleading toast. Update the logic around the product lookup in the
edit component to explicitly handle a missing result before accessing fields
like product.name, similar to the setProduct(found ?? null) guard in
client/app/product/[id].tsx. In the not-found branch, set the appropriate state
for a missing product and surface a clear not-found message instead of relying
on the exception handler.

In `@client/app/admin/products/index.tsx`:
- Around line 87-91: The product card render in the admin products list is
assuming `sizes` and `price` are always present, which can crash the whole
screen when API data is incomplete. Update the JSX in
`client/app/admin/products/index.tsx` around the product details block to safely
handle `product.sizes` and `product.price` in the same way `category` already
has a fallback. Use the existing product rendering logic in the list item to
show a default or placeholder when `sizes` is missing and avoid calling
`.toFixed` unless `price` is a valid number.
- Around line 80-84: The product thumbnail fallback in the admin products screen
still uses the discontinued via.placeholder.com URL, which causes broken images.
Update the Image source fallback in the products list rendering to use an active
placeholder such as placehold.co or switch to a local bundled asset, keeping the
change in the same Image component that selects from product.images.

In `@client/app/checkout.tsx`:
- Line 78: The text utility class on the selected address label is misspelled,
so the intended font size style won’t apply. Update the className in the
checkout text element that renders selectedAddress.type from the current typo to
the correct Tailwind text utility, keeping the rest of the styling unchanged.

In `@client/app/orders/`[id].tsx:
- Around line 12-18: Normalize the route param before using it in
fetchOrderDetails: useLocalSearchParams can return id as string or string[], so
the strict comparison against order._id in the dummyOrders.find lookup may fail
when id is an array. Update the [id] screen to derive a single string value from
id (for example, in the same scope as useLocalSearchParams) and use that
normalized value in the fetchOrderDetails comparison so order resolves
correctly.

---

Nitpick comments:
In `@client/app/admin/orders.tsx`:
- Around line 42-47: The `updateStatus` flow in `orders.tsx` never sets the
`updating` state to true, so the spinner branch in the modal cannot be reached.
Update the `updateStatus` handler to mark `setUpdating(true)` before starting
the async status change, and ensure it is always cleared afterward (preferably
in a `finally` block) around the real update logic so the modal loading UI
reflects the request state.

In `@client/app/admin/products/add.tsx`:
- Around line 40-49: The create-product submit flow in handleSubmit is
incomplete: it only validates fields and returns, so submitting is never set and
no product is persisted. Update handleSubmit in add.tsx to mirror the edit
screen’s submit path by setting submitting true, building the payload/FormData,
handling image upload and create request, then clearing submitting in finally so
the button spinner/disabled state in the submit UI is actually used.
- Around line 26-31: Update the ImagePicker usage in add.tsx to remove the
deprecated ImagePicker.MediaTypeOptions.Images value and use the new
string-array form for mediaTypes instead; locate the call to
ImagePicker.launchImageLibraryAsync and change the mediaTypes option to the
equivalent ['images'] value while keeping the rest of the selection options
unchanged.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: cfca7576-f8a8-4c9c-a52d-97bbc78ea614

📥 Commits

Reviewing files that changed from the base of the PR and between 35dd8ba and 5122f4e.

⛔ Files ignored due to path filters (1)
  • client/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (12)
  • client/app/addresses/index.tsx
  • client/app/admin/_layout.tsx
  • client/app/admin/index.tsx
  • client/app/admin/orders.tsx
  • client/app/admin/products/_layout.tsx
  • client/app/admin/products/add.tsx
  • client/app/admin/products/edit/[id].tsx
  • client/app/admin/products/index.tsx
  • client/app/checkout.tsx
  • client/app/orders/[id].tsx
  • client/app/orders/index.tsx
  • client/package.json

Comment on lines +33 to +35
const fetchAddresses = async () => {
setAddresses(dummyAddress as any);
setLoading(false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Address type contract is bypassed with as any and can drift UI state.

Line 34 suppresses a real contract mismatch: shared type allows "Work" but dummy data includes "Office", which breaks the label-selection flow. Normalize the source values or align the union instead of casting.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/app/addresses/index.tsx` around lines 33 - 35, The address mock data
in fetchAddresses is bypassing the shared type contract with an any cast, which
lets invalid values like "Office" slip into UI state. Remove the as any cast and
either normalize the dummyAddress source values to the existing Address type or
update the shared union to include the correct label, so the data passed into
setAddresses matches the contract used by the label-selection flow.

Comment on lines +51 to +55
const handleSaveAddress = async () => {
setModalVisible(false);
resetForm();
fetchAddresses();
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Save action does not persist add/edit changes.

Lines 51-55 only close/reset/refetch, so user edits are discarded and new addresses are never added.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/app/addresses/index.tsx` around lines 51 - 55, The handleSaveAddress
flow only closes the modal, resets the form, and refetches addresses, so it
never persists add/edit changes. Update handleSaveAddress in the addresses
screen to call the actual create/update logic using the current form values
before closing, and only reset/refetch after the save succeeds. Use the existing
handleSaveAddress, resetForm, and fetchAddresses flow to wire in the missing
persistence path.

Comment on lines +57 to +59
const handleDeleteAddress = async (id: string) => {

};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Delete button is wired to a no-op handler.

Line 57 defines an empty delete handler, but Line 111 invokes it from the UI, so delete never works.

Also applies to: 111-113

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/app/addresses/index.tsx` around lines 57 - 59, The delete flow is
currently a no-op because handleDeleteAddress is empty even though the UI calls
it from the delete button. Update handleDeleteAddress in the addresses index
component to perform the actual delete action (and any needed state refresh or
API call), and make sure the button’s onClick remains wired to that real handler
so deleting an address works end to end.

<Text className="text-xs text-secondary font-bold mb-1">CUSTOMER</Text>
<Text className="text-primary font-medium">{order.user?.name || 'Unknown User'}</Text>
<Text className="text-secondary text-xs">{order.user?.email || 'No email'}</Text>
{!order.user && <Text className="text-xs text-gray-400 mt-1">ID: {order.user?._id || 'N/A'}</Text>}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Contradictory, unreachable conditional render.

This block renders only when order.user is falsy, yet then reads order.user?._id — which can only ever resolve to 'N/A'. It's also unreachable since fetchOrders (lines 20-23) always sets user: dummyUser. Either drop this line or fix the condition to display the ID when the user object lacks a name.

🔧 Proposed fix
-                                {!order.user && <Text className="text-xs text-gray-400 mt-1">ID: {order.user?._id || 'N/A'}</Text>}
+                                {order.user?._id && <Text className="text-xs text-gray-400 mt-1">ID: {order.user._id}</Text>}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{!order.user && <Text className="text-xs text-gray-400 mt-1">ID: {order.user?._id || 'N/A'}</Text>}
{order.user?._id && <Text className="text-xs text-gray-400 mt-1">ID: {order.user._id}</Text>}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/app/admin/orders.tsx` at line 79, The conditional render in orders.tsx
is contradictory because it only runs when order.user is falsy, but then reads
order.user?._id and always falls back to N/A; update the JSX in the order row to
either remove this unreachable ID line or change the condition so the ID is
shown when the user exists but is missing a name, and keep the logic aligned
with fetchOrders/dummyUser so the display matches the actual data shape.

Comment on lines +33 to +48
try {
const product: any = dummyProducts.find((p) => p._id === id);
setName(product.name);
setDescription(product.description || "");
setPrice(product.price.toString());
setStock(product.stock.toString());
setCategory(typeof product.category === 'object' ? product.category.name : product.category);
setIsFeatured(product.isFeatured);

if (product.sizes) setSizes(Array.isArray(product.sizes) ? product.sizes.join(", ") : product.sizes);

if (product.images && Array.isArray(product.images)) {
setExistingImages(product.images);
} else if (product.images) {
setExistingImages([product.images]);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Handle "product not found" explicitly instead of relying on the catch block.

If dummyProducts.find returns undefined (unknown id), product.name throws a TypeError that's swallowed by the catch, surfacing a misleading "Failed to Fetch Product" toast. The reference implementation in client/app/product/[id].tsx guards with setProduct(found ?? null). Add an explicit not-found branch here.

🛡️ Proposed guard
                 const product: any = dummyProducts.find((p) => p._id === id);
+                if (!product) {
+                    Toast.show({ type: 'error', text1: 'Product Not Found' });
+                    router.back();
+                    return;
+                }
                 setName(product.name);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
const product: any = dummyProducts.find((p) => p._id === id);
setName(product.name);
setDescription(product.description || "");
setPrice(product.price.toString());
setStock(product.stock.toString());
setCategory(typeof product.category === 'object' ? product.category.name : product.category);
setIsFeatured(product.isFeatured);
if (product.sizes) setSizes(Array.isArray(product.sizes) ? product.sizes.join(", ") : product.sizes);
if (product.images && Array.isArray(product.images)) {
setExistingImages(product.images);
} else if (product.images) {
setExistingImages([product.images]);
}
try {
const product: any = dummyProducts.find((p) => p._id === id);
if (!product) {
Toast.show({ type: 'error', text1: 'Product Not Found' });
router.back();
return;
}
setName(product.name);
setDescription(product.description || "");
setPrice(product.price.toString());
setStock(product.stock.toString());
setCategory(typeof product.category === 'object' ? product.category.name : product.category);
setIsFeatured(product.isFeatured);
if (product.sizes) setSizes(Array.isArray(product.sizes) ? product.sizes.join(", ") : product.sizes);
if (product.images && Array.isArray(product.images)) {
setExistingImages(product.images);
} else if (product.images) {
setExistingImages([product.images]);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/app/admin/products/edit/`[id].tsx around lines 33 - 48, The product
load flow in the edit page currently assumes dummyProducts.find always returns a
product, so an unknown id falls into the catch with a misleading toast. Update
the logic around the product lookup in the edit component to explicitly handle a
missing result before accessing fields like product.name, similar to the
setProduct(found ?? null) guard in client/app/product/[id].tsx. In the not-found
branch, set the appropriate state for a missing product and surface a clear
not-found message instead of relying on the exception handler.

Comment on lines +80 to +84
<Image
source={{ uri: product.images && product.images.length > 0 ? product.images[0] : 'https://via.placeholder.com/150' }}
className="w-16 h-16 rounded-lg bg-gray-100 mr-3"
resizeMode="cover"
/>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

Is via.placeholder.com placeholder image service still operational in 2026?

💡 Result:

The via.placeholder.com placeholder image service is not operational in 2026 [1][2][3]. The service was effectively decommissioned following the acquisition of the placeholder.com domain in 2023 [2][3][4]. Since 2024, the domain has been largely unresponsive, failing to resolve or returning connection errors (such as connection refused or timeouts) for requests [1][5][3]. Extensive reports from 2025 and 2026 indicate that the service is abandoned [1][2]. Developers are actively removing references to via.placeholder.com from codebases, documentation, and automated tests, typically replacing it with functional alternatives like placehold.co, which provides a similar URL-based API [5][6][7][8][9]. If you encounter broken images in older projects, you should replace the via.placeholder.com domain with an active, maintained service [1][3].

Citations:


🏁 Script executed:

head -n 100 /tmp/logs/full_output.log

Repository: Ayushman8/ecommerce-app

Length of output: 246


Replace the discontinued via.placeholder.com URL

The via.placeholder.com service has been decommissioned and no longer resolves, causing broken image thumbnails. Replace the fallback URL with an active alternative like https://placehold.co, for example:

source={{ uri: product.images && product.images.length > 0 ? product.images[0] : 'https://placehold.co/150' }}

Or use a locally bundled placeholder asset to avoid external dependencies.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/app/admin/products/index.tsx` around lines 80 - 84, The product
thumbnail fallback in the admin products screen still uses the discontinued
via.placeholder.com URL, which causes broken images. Update the Image source
fallback in the products list rendering to use an active placeholder such as
placehold.co or switch to a local bundled asset, keeping the change in the same
Image component that selects from product.images.

Comment on lines +87 to +91
<Text className="font-bold text-primary text-base" numberOfLines={1}>{product.name}</Text>
<Text className="text-secondary text-xs mb-1" numberOfLines={1}>Category : {product.category || 'Others'}</Text>
<Text className="text-secondary text-xs mb-1" numberOfLines={1}>Stock : {product.stock}</Text>
<Text className="text-secondary text-xs mb-1" numberOfLines={1}>Sizes : {product.sizes.join(", ")}</Text>
<Text className="text-primary font-bold">${product.price.toFixed(2)}</Text>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Guard against missing sizes/price to avoid render crashes.

Unlike category (which has an || 'Others' fallback), product.sizes.join(", ") and product.price.toFixed(2) assume those fields always exist. If any product lacks sizes or price, .join/.toFixed throws and the whole list fails to render. Once this screen is backed by real API data instead of dummyProducts, this becomes reachable.

🛡️ Proposed guards
-                                <Text className="text-secondary text-xs mb-1" numberOfLines={1}>Stock : {product.stock}</Text>
-                                <Text className="text-secondary text-xs mb-1" numberOfLines={1}>Sizes : {product.sizes.join(", ")}</Text>
-                                <Text className="text-primary font-bold">${product.price.toFixed(2)}</Text>
+                                <Text className="text-secondary text-xs mb-1" numberOfLines={1}>Stock : {product.stock ?? 0}</Text>
+                                <Text className="text-secondary text-xs mb-1" numberOfLines={1}>Sizes : {Array.isArray(product.sizes) ? product.sizes.join(", ") : "-"}</Text>
+                                <Text className="text-primary font-bold">${Number(product.price ?? 0).toFixed(2)}</Text>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Text className="font-bold text-primary text-base" numberOfLines={1}>{product.name}</Text>
<Text className="text-secondary text-xs mb-1" numberOfLines={1}>Category : {product.category || 'Others'}</Text>
<Text className="text-secondary text-xs mb-1" numberOfLines={1}>Stock : {product.stock}</Text>
<Text className="text-secondary text-xs mb-1" numberOfLines={1}>Sizes : {product.sizes.join(", ")}</Text>
<Text className="text-primary font-bold">${product.price.toFixed(2)}</Text>
<Text className="font-bold text-primary text-base" numberOfLines={1}>{product.name}</Text>
<Text className="text-secondary text-xs mb-1" numberOfLines={1}>Category : {product.category || 'Others'}</Text>
<Text className="text-secondary text-xs mb-1" numberOfLines={1}>Stock : {product.stock ?? 0}</Text>
<Text className="text-secondary text-xs mb-1" numberOfLines={1}>Sizes : {Array.isArray(product.sizes) ? product.sizes.join(", ") : "-"}</Text>
<Text className="text-primary font-bold">${Number(product.price ?? 0).toFixed(2)}</Text>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/app/admin/products/index.tsx` around lines 87 - 91, The product card
render in the admin products list is assuming `sizes` and `price` are always
present, which can crash the whole screen when API data is incomplete. Update
the JSX in `client/app/admin/products/index.tsx` around the product details
block to safely handle `product.sizes` and `product.price` in the same way
`category` already has a fallback. Use the existing product rendering logic in
the list item to show a default or placeholder when `sizes` is missing and avoid
calling `.toFixed` unless `price` is a valid number.

Comment thread client/app/checkout.tsx
{selectedAddress ? (
<View className='bg-white p-4 rounded-xl mb-6 shadow-sm'>
<View className='flex-row items-center justify-between mb-2'>
<Text className='test-base font-bold'>{selectedAddress.type}</Text>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Fix typo in text utility class.

Line 78 uses test-base; this should be text-base or the intended text style won’t apply.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/app/checkout.tsx` at line 78, The text utility class on the selected
address label is misspelled, so the intended font size style won’t apply. Update
the className in the checkout text element that renders selectedAddress.type
from the current typo to the correct Tailwind text utility, keeping the rest of
the styling unchanged.

Comment on lines +12 to +18
const { id } = useLocalSearchParams();
const [order, setOrder] = useState<Order | null>(null);
const [loading, setLoading] = useState(true);

const fetchOrderDetails = async () => {
setOrder(dummyOrders.find((order) => order._id === id) as any);
setLoading(false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the implementation or usage pattern of useLocalSearchParams
# Also check the project structure for related type definitions
ast-grep outline client/app/orders/[id].tsx

Repository: Ayushman8/ecommerce-app

Length of output: 233


🏁 Script executed:

#!/bin/bash
# Search for useLocalSearchParams definition or imports
rg -n "useLocalSearchParams" client/app/orders/[id].tsx

Repository: Ayushman8/ecommerce-app

Length of output: 261


🏁 Script executed:

#!/bin/bash
# Find dummyOrders definition to check the type of _id
rg -n "dummyOrders" client/app/orders/[id].tsx --context 5

Repository: Ayushman8/ecommerce-app

Length of output: 938


🏁 Script executed:

#!/bin/bash
# Check the type of useLocalSearchParams return value and the structure of dummyOrders
# First, look for the types file
cat -n client/constants/types.ts

Repository: Ayushman8/ecommerce-app

Length of output: 3641


🏁 Script executed:

#!/bin/bash
# Check the assets file where dummyOrders is defined
cat -n client/assets/assets.ts

Repository: Ayushman8/ecommerce-app

Length of output: 50379


🏁 Script executed:

#!/bin/bash
# Search for useLocalSearchParams type definition in expo-router to understand return type
rg -n "useLocalSearchParams" node_modules/expo-router/types -A 10 | head -100

Repository: Ayushman8/ecommerce-app

Length of output: 232


🏁 Script executed:

#!/bin/bash
# Check the file without dummyOrders products data
cat -n client/assets/assets.ts | sed -n '1106,1200p'

Repository: Ayushman8/ecommerce-app

Length of output: 3364


🏁 Script executed:

#!/bin/bash
# Check the expo-router documentation or type definitions in package.json or node_modules
# We need to verify if useLocalSearchParams returns string or string[]
# First check package.json to see which version of expo-router is being used
cat package.json | grep -E "(expo-router|`@expo`)"

Repository: Ayushman8/ecommerce-app

Length of output: 206


🏁 Script executed:

#!/bin/bash
# Look for type definitions in `@expo` or expo-router related packages
# Try to find where the types are defined
if [ -d "node_modules/@expo" ]; then
    find node_modules/@expo -name "useLocalSearchParams*" -type f 2>/dev/null | head -5
fi
if [ -d "node_modules/expo-router" ]; then
    find node_modules/expo-router -name "*.d.ts" -type f 2>/dev/null | head -5
fi

Repository: Ayushman8/ecommerce-app

Length of output: 161


🏁 Script executed:

#!/bin/bash
# Search for how useLocalSearchParams is typically used in the codebase to infer behavior
# Check if there's any usage showing it can return string[]
rg -n "useLocalSearchParams" client --context 3

Repository: Ayushman8/ecommerce-app

Length of output: 5075


🏁 Script executed:

cat -n client/app/orders/[id].tsx

Repository: Ayushman8/ecommerce-app

Length of output: 9166


🏁 Script executed:

python3 - <<'PY'
# Verify the comparison behavior: string vs string[] vs number
# This simulates what happens when comparing order._id (string) with id from useLocalSearchParams

# Scenario 1: normal string comparison
order_id = "69895582ef9d0c746ef82849"
param_id_regular = "69895582ef9d0c746ef82849"
param_id_array = ["69895582ef9d0c746ef82849"]

print(f"Regular string comparison: {order_id == param_id_regular}")
print(f"Array comparison: {order_id == param_id_array}")

# Scenario 2: what if the param was an array?
try:
    result = order_id == param_id_array
    print(f"Result type when comparing string to list: {type(result)}, value: {result}")
except Exception as e:
    print(f"Error: {e}")

# Scenario 3: Does JavaScript/TypeScript coerce array to string?
# In JS: [ "abc" ] == "abc" -> true, but === "abc" -> false
print(f"JS-like coercion check (Python equivalent):")
print(f"str(['abc']) == 'abc': {str(['abc']) == 'abc'}")  # Should be false
print(f"['abc'] == '[' + 'abc' + ']'': {str(['abc']) == \"['abc']\"}")  # Should be true

# Conclusion: strict equality (===) between string and string[] is always false
PY

Repository: Ayushman8/ecommerce-app

Length of output: 420


🏁 Script executed:

python3 - <<'PY'
# Verify the comparison behavior: string vs string[] vs number
# This simulates what happens when comparing order._id (string) with id from useLocalSearchParams

order_id = "69895582ef9d0c746ef82849"
param_id_regular = "69895582ef9d0c746ef82849"
param_id_array = ["69895582ef9d0c746ef82849"]

print(f"Regular string comparison: {order_id == param_id_regular}")
arr_string_repr = str(param_id_array)
print(f"Array string representation: {arr_string_repr}")
print(f"String comparison with array rep: {order_id == arr_string_repr}")

# Conclusion: strict equality (===) between string and string[] is always false in TS
print("In TypeScript, comparing string 'abc' to string[] ['abc'] using === returns false.")
PY

Repository: Ayushman8/ecommerce-app

Length of output: 374


🏁 Script executed:

#!/bin/bash
# Let's check if expo-router's useLocalSearchParams can return an array if multiple params with same name exist
# Look at the expo-router package.json or README
find . -name "package.json" -path "*/expo-router/*" 2>/dev/null | head -1 | xargs cat | grep -E "(name|version)" | head -5

Repository: Ayushman8/ecommerce-app

Length of output: 161


🏁 Script executed:

python3 - <<'PY'
# According to expo-router documentation, useLocalSearchParams returns a Record<string, string | string[]>
# where string[] happens when multiple query parameters share the same key.
# Since [id] is a route parameter (dynamic segment), not a query param, 
# the behavior may differ. Let's check the route structure.
print("Checking route behavior for [id] dynamic segment:")
print("In expo-router, dynamic route params like [id] are typically strings,")
print("but the return type is documented as Record<string, string | string[]>.")
print("Without explicit type assertion or schema validation, `id` could legally be string[].")
print("A safe normalization is recommended.")
PY

Repository: Ayushman8/ecommerce-app

Length of output: 476


🌐 Web query:

expo-router useLocalSearchParams return type dynamic route parameter

💡 Result:

In Expo Router, the useLocalSearchParams hook returns an object containing all URL parameters for the contextually focused route, which includes both dynamic path segments and search (query) parameters [1][2][3]. ### Return Type and Type Safety By default, if no generic is provided, the hook returns Record<string, string | string[]>, representing the key-value pairs of the URL parameters [2]. To achieve strong type safety for dynamic route parameters, you can provide a generic type argument to the hook [4][5][6]. 1. Using a Route String: You can pass a string literal representing the route (e.g., '/[user]') as the first generic argument. This allows Expo Router to automatically infer and strictly type the parameters based on that route [5][6][7]. tsx // Automatically typed as { user: string } const { user } = useLocalSearchParams<'/[user]'>(); 2. Manual Typing: If you need to include custom search parameters that are not part of the file system route, you can provide an additional type object as the second generic argument [5][6][7]. tsx // user is typed from route, query is typed manually const { user, query } = useLocalSearchParams<'/[user]', { query?: string }>(); ### Important Considerations * Dynamic route parameters (e.g., [user]) are guaranteed to be present and non-nullish when the route is matched [8][9]. * Catch-all route parameters (e.g., [...slug]) will be typed as an array of strings (string[]) [4][5][6]. * When referencing routes in the generic, ensure you use the relative or absolute path correctly according to your project structure (e.g., use '/users/[userId]' rather than the full file path 'app/users/[userId]/index.tsx') [10]. * The hook returns RouteParams<TRoute> & TParams, where RouteParams corresponds to the path segments and TParams corresponds to the manually defined search parameters [1][2][3].

Citations:


Normalize route param type to prevent lookup failure.
useLocalSearchParams() returns Record<string, string | string[]>. Declaring id as string | string[] causes the strict equality check (===) on Line 17 to fail if the parameter is passed as an array (which can happen with certain routing navigations), resulting in order always being null.

Normalize id to a string before the lookup to ensure order._id is always compared against a single string value.

client/app/orders/[id].tsx

Code Snippet
const { id } = useLocalSearchParams();
// Normalize id to a string to handle potential string[]
const orderId = Array.isArray(id) ? id[0] : id;

// ... in fetchOrderDetails
setOrder(dummyOrders.find((order) => order._id === orderId) as any);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/app/orders/`[id].tsx around lines 12 - 18, Normalize the route param
before using it in fetchOrderDetails: useLocalSearchParams can return id as
string or string[], so the strict comparison against order._id in the
dummyOrders.find lookup may fail when id is an array. Update the [id] screen to
derive a single string value from id (for example, in the same scope as
useLocalSearchParams) and use that normalized value in the fetchOrderDetails
comparison so order resolves correctly.

@Ayushman8 Ayushman8 merged commit db83929 into main Jun 25, 2026
1 check passed
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.

1 participant