Skip to content

Remove useEffect from account#72

Merged
damianlegawiec merged 4 commits intomainfrom
fix/remove-use-effect-usage
Mar 19, 2026
Merged

Remove useEffect from account#72
damianlegawiec merged 4 commits intomainfrom
fix/remove-use-effect-usage

Conversation

@damianlegawiec
Copy link
Member

@damianlegawiec damianlegawiec commented Mar 18, 2026

Summary by CodeRabbit

  • New Features

    • Consolidated address management component with add/edit/delete flows, credit card list with removal flow, gift card list with copy and balance progress, detailed order view, and an orders list for browsing.
    • New date/time and payment/fulfillment status formatting utilities for consistent display.
  • Refactor

    • Converted multiple account pages to server-rendered async pages, simplifying data loading, state handling, and empty-state rendering for a more consistent experience.

@coderabbitai
Copy link

coderabbitai bot commented Mar 18, 2026

Walkthrough

Several account pages were converted from client components to server-side async pages; server pages now fetch data and delegate interactive flows to new client components (AddressManagement, CreditCardList, GiftCardList, OrderList, OrderDetail), plus shared formatting utilities were added.

Changes

Cohort / File(s) Summary
Addresses
src/app/[country]/[locale]/(storefront)/account/addresses/page.tsx, src/components/addresses/AddressManagement.tsx
Converted Addresses page to an async server component that fetches addresses and countries in parallel; introduced AddressManagement client component to handle add/edit/delete flows and modal UI; removed prior client-only handlers and subcomponents.
Credit Cards
src/app/[country]/[locale]/(storefront)/account/credit-cards/page.tsx, src/components/account/CreditCardList.tsx
Moved credit-cards page server-side and added CreditCardList client component to render cards and manage per-card deletion with confirmation and local state updates.
Gift Cards
src/app/[country]/[locale]/(storefront)/account/gift-cards/page.tsx, src/components/account/GiftCardList.tsx
Gift cards page now fetches on the server and delegates rendering to GiftCardList, which provides copy-to-clipboard, active/inactive grouping, progress/display, and per-item UI.
Orders — list & detail
src/app/[country]/[locale]/(storefront)/account/orders/page.tsx, src/app/[country]/[locale]/(storefront)/account/orders/[id]/page.tsx, src/components/account/OrderList.tsx, src/components/account/OrderDetail.tsx
Converted orders pages to async server components; added OrderList for table rendering and OrderDetail for detailed order/fulfillment views; moved formatting/status logic into shared utilities and components; basePath derivation moved to server.
Formatting utilities
src/lib/utils/format.ts
Added formatDate, formatDateTime, getPaymentStatusColor, and getFulfillmentStatusColor for consistent date and status rendering used by order/list/detail components.

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant ServerPage
  participant ClientComponent
  participant SDK_API
  participant DB

  Browser->>ServerPage: Request account page (SSR)
  ServerPage->>SDK_API: get*() calls (addresses/cards/gift-cards/orders) (parallel)
  SDK_API->>DB: query data
  DB-->>SDK_API: data
  SDK_API-->>ServerPage: response
  ServerPage-->>Browser: render page with initial props (e.g., initialAddresses)

  Note right of Browser: Interactive flows handled client-side
  Browser->>ClientComponent: user action (add/edit/delete)
  ClientComponent->>SDK_API: create/update/delete API call
  SDK_API->>DB: persist change
  DB-->>SDK_API: result
  SDK_API-->>ClientComponent: result
  ClientComponent-->>Browser: update local UI state
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Bump SDK to 0.7.1 #49 — Touches the same Addresses page and SDK types (Address/Country) used by the AddressesPage refactor.
  • Use Shadcn #50 — Modifies the Addresses page imports/UI and may conflict with the AddressManagement replacement.
  • Bump to Spree SDK 0.12.0 #68 — Renames/changes around fulfillment/shipment and order typings that overlap with OrderDetail/OrderList changes.

Poem

🐰 I hopped from client into server today,
fetching early so the pages could stay,
Cards, gifts, and orders all tidy and bright,
I nibble on code through the soft midnight. 🥕✨

🚥 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 accurately describes the main objective of the pull request, which is to eliminate useEffect hooks from account-related pages and components by converting them to server components and delegating UI logic to extracted client components.

✏️ 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 fix/remove-use-effect-usage
📝 Coding Plan
  • Generate coding plan for human review comments

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

@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 (1)
src/app/[country]/[locale]/(storefront)/account/addresses/page.tsx (1)

6-6: Consider adding page metadata for consistency.

As per coding guidelines, page files should use the generateMetadata function for SEO. While this account page may not need search indexing, adding basic metadata improves consistency and browser tab titles.

📝 Example metadata
import type { Metadata } from "next";

export const metadata: Metadata = {
  title: "Addresses",
  description: "Manage your saved addresses",
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/`[country]/[locale]/(storefront)/account/addresses/page.tsx at line
6, Add basic page metadata for consistency by exporting a Metadata object or
implementing generateMetadata alongside the existing AddressesPage component;
declare and export either export const metadata: Metadata = { title:
"Addresses", description: "Manage your saved addresses" } or an async function
generateMetadata() that returns equivalent metadata so the browser tab title and
SEO fields are set for the AddressesPage export.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/addresses/AddressManagement.tsx`:
- Around line 35-39: The handleDelete function sets deleting state before
awaiting onDelete but doesn’t reset it if onDelete throws; wrap the await call
in a try/finally so setDeleting(false) always runs (i.e., keep setDeleting(true)
at start, then try { await onDelete(); } finally { setDeleting(false); }) to
ensure the deleting state is cleared even on errors—update the handleDelete
function to use this pattern.
- Around line 147-153: The handleDelete function currently ignores failures from
deleteAddress and gives no user feedback; update handleDelete to check the
returned result when deleteAddress(id) resolves and, if result.success is false,
surface the error to the user (e.g., call your toast/alert API or at minimum
console.error) instead of silently doing nothing; ensure you still only call
setAddresses((prev)=>prev.filter(...)) when result.success is true, and
optionally wrap the await deleteAddress(id) in a try/catch to handle unexpected
exceptions and report them via the same notification mechanism.

---

Nitpick comments:
In `@src/app/`[country]/[locale]/(storefront)/account/addresses/page.tsx:
- Line 6: Add basic page metadata for consistency by exporting a Metadata object
or implementing generateMetadata alongside the existing AddressesPage component;
declare and export either export const metadata: Metadata = { title:
"Addresses", description: "Manage your saved addresses" } or an async function
generateMetadata() that returns equivalent metadata so the browser tab title and
SEO fields are set for the AddressesPage export.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e4b92659-2f4e-4fb7-8e8a-316e8a5577b4

📥 Commits

Reviewing files that changed from the base of the PR and between 19e9277 and 61bd98d.

📒 Files selected for processing (2)
  • src/app/[country]/[locale]/(storefront)/account/addresses/page.tsx
  • src/components/addresses/AddressManagement.tsx

Copy link

@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: 3

🧹 Nitpick comments (2)
src/components/account/CreditCardList.tsx (1)

107-107: Prefer non-blocking error feedback over alert().

alert() works, but it blocks interaction and is jarring. Prefer your toast/inline notification pattern for delete failures.

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

In `@src/components/account/CreditCardList.tsx` at line 107, The alert() call in
CreditCardList.tsx (on failed delete: alert(`Failed to remove card:
${result.error}`)) is blocking and should be replaced with the app's
non-blocking notification pattern; update the failure branch in the remove-card
handler (e.g., the function handling the deletion in CreditCardList or its
handleRemoveCard/removeCard code path) to call the project's toast/notification
API (for example toast.error(...) or the useNotification/setNotification helper
used elsewhere) and include the result.error text in the message so the user
sees a non-blocking, contextual error notification.
src/app/[country]/[locale]/(storefront)/account/orders/page.tsx (1)

11-16: Start getOrders() before awaiting params.

These two awaits are independent here, so doing them serially adds avoidable latency to every request.

⚡ Suggested refactor
 export default async function OrdersPage({ params }: OrdersPageProps) {
-  const { country, locale } = await params;
-  const basePath = `/${country}/${locale}`;
-
-  const response = await getOrders({ limit: 50 });
+  const [{ country, locale }, response] = await Promise.all([
+    params,
+    getOrders({ limit: 50 }),
+  ]);
+  const basePath = `/${country}/${locale}`;
   const orders = response.data.filter((o) => o.completed_at !== null);
As per coding guidelines, "Use Promise.all() for parallel data fetching in server components instead of sequential awaits."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/`[country]/[locale]/(storefront)/account/orders/page.tsx around lines
11 - 16, OrdersPage currently awaits params and getOrders sequentially causing
extra latency; start the getOrders call before awaiting params and then await
both in parallel (e.g., initiate const ordersPromise = getOrders({ limit: 50 });
then await params and ordersPromise with Promise.all or await params then await
ordersPromise) so you still build basePath from params and compute orders =
response.data.filter(...) after the response resolves; update references to
getOrders, params, basePath, OrdersPage, and orders accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/app/`[country]/[locale]/(storefront)/account/orders/[id]/page.tsx:
- Around line 18-20: The order detail page calls getOrder(id) but does not
enforce the same "completed" filter used by the history list, allowing
draft/abandoned checkouts to render; update the page (page.tsx) after the
getOrder(id) call to verify the order matches the history-list predicate (e.g.
order.status === 'completed' or the exact condition used by the sibling history
code) and if it does not, return a proper non-found/redirect response (e.g. call
notFound() or redirect to the orders list) so only completed orders render on
/account/orders/[id].

In `@src/app/`[country]/[locale]/(storefront)/account/orders/page.tsx:
- Around line 15-16: The current call to getOrders({ limit: 50 }) applies the
limit before you filter out incomplete orders, causing completed history to be
truncated; update the fetching logic in page.tsx so completed orders are
selected at query time (e.g., pass a filter that requires completed_at != null
to getOrders) or implement pagination that continues requesting pages until you
have the desired count of completed orders before slicing; reference getOrders,
the limit: 50 argument, and the completed_at filter when making this change.

In `@src/lib/utils/format.ts`:
- Around line 10-18: The function formatDateTime currently calls
toLocaleDateString with time options which will throw a TypeError; change the
call to use toLocaleString instead and keep the same locale "en-US" and the
options object (year, month, day, hour, minute) so date and time are formatted
correctly; update the formatDateTime function to return new
Date(dateString).toLocaleString("en-US", { year: "numeric", month: "long", day:
"numeric", hour: "2-digit", minute: "2-digit" }) when dateString is present and
still return "-" when null/empty.

---

Nitpick comments:
In `@src/app/`[country]/[locale]/(storefront)/account/orders/page.tsx:
- Around line 11-16: OrdersPage currently awaits params and getOrders
sequentially causing extra latency; start the getOrders call before awaiting
params and then await both in parallel (e.g., initiate const ordersPromise =
getOrders({ limit: 50 }); then await params and ordersPromise with Promise.all
or await params then await ordersPromise) so you still build basePath from
params and compute orders = response.data.filter(...) after the response
resolves; update references to getOrders, params, basePath, OrdersPage, and
orders accordingly.

In `@src/components/account/CreditCardList.tsx`:
- Line 107: The alert() call in CreditCardList.tsx (on failed delete:
alert(`Failed to remove card: ${result.error}`)) is blocking and should be
replaced with the app's non-blocking notification pattern; update the failure
branch in the remove-card handler (e.g., the function handling the deletion in
CreditCardList or its handleRemoveCard/removeCard code path) to call the
project's toast/notification API (for example toast.error(...) or the
useNotification/setNotification helper used elsewhere) and include the
result.error text in the message so the user sees a non-blocking, contextual
error notification.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fe59f0f4-cb80-464f-9a54-c1e54675b080

📥 Commits

Reviewing files that changed from the base of the PR and between 61bd98d and e9a1157.

📒 Files selected for processing (10)
  • src/app/[country]/[locale]/(storefront)/account/credit-cards/page.tsx
  • src/app/[country]/[locale]/(storefront)/account/gift-cards/page.tsx
  • src/app/[country]/[locale]/(storefront)/account/orders/[id]/page.tsx
  • src/app/[country]/[locale]/(storefront)/account/orders/page.tsx
  • src/components/account/CreditCardList.tsx
  • src/components/account/GiftCardList.tsx
  • src/components/account/OrderDetail.tsx
  • src/components/account/OrderList.tsx
  • src/components/addresses/AddressManagement.tsx
  • src/lib/utils/format.ts

@damianlegawiec damianlegawiec changed the title Remove useEffect from account addresses Remove useEffect from account Mar 19, 2026
Copy link

@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 (1)
src/components/addresses/AddressManagement.tsx (1)

32-32: Add explicit return types to functions/handlers.

Please annotate return types for AddressCard, fetchStates, handleEdit, handleAdd, handleSave, and handleDelete instead of relying on inference.

🧩 Example
-function AddressCard({ address, onEdit, onDelete }: AddressCardProps) {
+function AddressCard({ address, onEdit, onDelete }: AddressCardProps): JSX.Element {
@@
-  const fetchStates = useCallback(async (countryIso: string) => {
+  const fetchStates = useCallback(async (countryIso: string): Promise<Country["states"]> => {
@@
-  const handleEdit = (address: Address) => {
+  const handleEdit = (address: Address): void => {
@@
-  const handleAdd = () => {
+  const handleAdd = (): void => {
@@
-  const handleSave = async (data: AddressParams, id?: string) => {
+  const handleSave = async (data: AddressParams, id?: string): Promise<void> => {
@@
-  const handleDelete = async (id: string) => {
+  const handleDelete = async (id: string): Promise<void> => {

As per coding guidelines: "Use strict TypeScript type checking. Always define explicit return types for functions..."

Also applies to: 107-157

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

In `@src/components/addresses/AddressManagement.tsx` at line 32, Add explicit
return type annotations for AddressCard, fetchStates, handleEdit, handleAdd,
handleSave, and handleDelete: annotate the React component AddressCard as
returning JSX.Element; annotate fetchStates with the correct Promise return
(e.g., Promise<State[]> if it returns data or Promise<void> if it only sets
state); and annotate all handlers (handleEdit, handleAdd, handleSave,
handleDelete) as returning void or Promise<void> as appropriate. Update the
function signatures for these symbols to include the explicit return types to
satisfy strict TypeScript checking.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/app/`[country]/[locale]/(storefront)/account/orders/page.tsx:
- Around line 15-16: The order list is including incomplete orders (completed_at
=== null) which leads to "Order not found" on details; fix by ensuring only
completed orders are shown: either update the API call getOrders to accept and
send a filter for completed_at (e.g., pass a param to only fetch orders with
completed_at !== null) or restore the client-side filtering before rendering
(filter the orders array returned from getOrders by completed_at !== null) and
then pass the filtered array to OrderList so the detail guard no longer
triggers.

In `@src/components/addresses/AddressManagement.tsx`:
- Around line 159-174: The empty-state rendering currently relies solely on the
immutable prop emptyState which can leave the UI stuck after creating an
address; update the render gating to use the live addresses collection instead
(e.g. check addresses.length === 0) so the component exits the empty state as
soon as addresses contains items. Locate the conditional that returns the Add
Your First Address button and AddressEditModal (the block referencing
emptyState, handleAdd, modalOpen, AddressEditModal and editingAddress) and
replace its predicate with a runtime check against addresses.length (or combine
both: emptyState && addresses.length === 0) so the UI immediately shows the
address grid after a successful create.

---

Nitpick comments:
In `@src/components/addresses/AddressManagement.tsx`:
- Line 32: Add explicit return type annotations for AddressCard, fetchStates,
handleEdit, handleAdd, handleSave, and handleDelete: annotate the React
component AddressCard as returning JSX.Element; annotate fetchStates with the
correct Promise return (e.g., Promise<State[]> if it returns data or
Promise<void> if it only sets state); and annotate all handlers (handleEdit,
handleAdd, handleSave, handleDelete) as returning void or Promise<void> as
appropriate. Update the function signatures for these symbols to include the
explicit return types to satisfy strict TypeScript checking.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c16092c2-7d07-4056-a1b3-5d69dcd11ece

📥 Commits

Reviewing files that changed from the base of the PR and between e9a1157 and aea0706.

📒 Files selected for processing (4)
  • src/app/[country]/[locale]/(storefront)/account/orders/[id]/page.tsx
  • src/app/[country]/[locale]/(storefront)/account/orders/page.tsx
  • src/components/addresses/AddressManagement.tsx
  • src/lib/utils/format.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/lib/utils/format.ts

Copy link

@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 (2)
src/components/addresses/AddressManagement.tsx (2)

26-30: Type signature for onDelete should reflect async nature.

The onDelete callback is awaited on line 38, and the actual function passed (line 193) returns Promise<void>. Update the type to accurately reflect this.

✏️ Suggested fix
 interface AddressCardProps {
   address: Address;
   onEdit: () => void;
-  onDelete: () => void;
+  onDelete: () => Promise<void>;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/addresses/AddressManagement.tsx` around lines 26 - 30, Update
the AddressCardProps type so the onDelete callback reflects that it returns a
Promise; change its signature from onDelete: () => void to onDelete: () =>
Promise<void> (and update any related usages if necessary) so the await on
onDelete in the AddressCard component matches the actual async function being
passed.

126-148: Non-null assertions can be avoided by restructuring.

The result.address! assertions (lines 135, 145) are safe since they're inside if (result.address) blocks, but the pattern can be simplified.

✏️ Suggested refactor
     if (id) {
       const result = await updateAddress(id, data);
       if (!result.success) {
         throw new Error(result.error);
       }
-      // Update just the one address in state
-      if (result.address) {
-        setAddresses((prev) =>
-          prev.map((addr) => (addr.id === id ? result.address! : addr)),
-        );
-      }
+      // Update just the one address in state
+      const { address } = result;
+      if (address) {
+        setAddresses((prev) =>
+          prev.map((addr) => (addr.id === id ? address : addr)),
+        );
+      }
     } else {
       const result = await createAddress(data);
       if (!result.success) {
         throw new Error(result.error);
       }
-      // Append the new address
-      if (result.address) {
-        setAddresses((prev) => [...prev, result.address!]);
-      }
+      // Append the new address
+      const { address } = result;
+      if (address) {
+        setAddresses((prev) => [...prev, address]);
+      }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/addresses/AddressManagement.tsx` around lines 126 - 148, The
code in handleSave uses non-null assertions result.address! inside branches that
already check if (result.address); remove the assertions by restructuring to use
the narrowed variable directly — for example, within the if (result.address)
blocks, store const addr = result.address and pass addr to setAddresses (in the
update branch map replacement for addr.id and in the create branch appending
addr) so you avoid the ! operator and keep type safety; references: handleSave,
updateAddress, createAddress, setAddresses, result.address.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/components/addresses/AddressManagement.tsx`:
- Around line 26-30: Update the AddressCardProps type so the onDelete callback
reflects that it returns a Promise; change its signature from onDelete: () =>
void to onDelete: () => Promise<void> (and update any related usages if
necessary) so the await on onDelete in the AddressCard component matches the
actual async function being passed.
- Around line 126-148: The code in handleSave uses non-null assertions
result.address! inside branches that already check if (result.address); remove
the assertions by restructuring to use the narrowed variable directly — for
example, within the if (result.address) blocks, store const addr =
result.address and pass addr to setAddresses (in the update branch map
replacement for addr.id and in the create branch appending addr) so you avoid
the ! operator and keep type safety; references: handleSave, updateAddress,
createAddress, setAddresses, result.address.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 88fc5e12-4d7e-4c50-8c47-7ae4d62ea7f0

📥 Commits

Reviewing files that changed from the base of the PR and between aea0706 and 7a7552b.

📒 Files selected for processing (2)
  • src/app/[country]/[locale]/(storefront)/account/orders/page.tsx
  • src/components/addresses/AddressManagement.tsx
✅ Files skipped from review due to trivial changes (1)
  • src/app/[country]/[locale]/(storefront)/account/orders/page.tsx

@damianlegawiec damianlegawiec merged commit 6b763b1 into main Mar 19, 2026
4 checks 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