Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
WalkthroughIntroduces a centralized Utils.CartStorage utility and migrates cart ID persistence from ad-hoc localStorage + Changes
Sequence Diagram(s)sequenceDiagram
participant GP as GlobalProvider
participant Comp as UI Component (Cart/Product/Checkout)
participant Utils as Utils.CartStorage
participant LS as localStorage
GP->>Utils: configureCartStorage({storageKey, orgId})
Note right of Utils: stores config in module scope
Comp->>Utils: getCartId()
Utils->>LS: read(storageKey) rgba(200,200,255,0.5)
LS-->>Utils: JSON map
Utils-->>Comp: cartId | null
Comp->>Utils: setCartId(cartId)
Utils->>LS: write(updated JSON map) rgba(200,200,255,0.5)
LS-->>Utils: ack
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Coverage Report for packages/configs/vitest-config
File CoverageNo changed files found. |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
packages/ui/src/providers/GlobalProvider/GlobalProvider.tsx (1)
81-86: Session storage key is hardcoded — consider extracting as a constant.The
'wasAuthenticated'key is used in two places within this effect. For maintainability and to avoid typos, consider extracting it to a named constant.♻️ Suggested refactor
+'use client'; + +const WAS_AUTHENTICATED_KEY = 'wasAuthenticated'; + // ... existing code ... useEffect(() => { Utils.CartStorage.configureCartStorage({ storageKey: cartStorageKey, orgId: user?.orgId }); if (user?.orgId) { - sessionStorage.setItem('wasAuthenticated', '1'); - } else if (sessionStorage.getItem('wasAuthenticated')) { - sessionStorage.removeItem('wasAuthenticated'); + sessionStorage.setItem(WAS_AUTHENTICATED_KEY, '1'); + } else if (sessionStorage.getItem(WAS_AUTHENTICATED_KEY)) { + sessionStorage.removeItem(WAS_AUTHENTICATED_KEY); Utils.CartStorage.removeAllCartIds(); } }, [cartStorageKey, user?.orgId]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/ui/src/providers/GlobalProvider/GlobalProvider.tsx` around lines 81 - 86, Extract the hardcoded sessionStorage key 'wasAuthenticated' into a descriptive constant (e.g., WAS_AUTHENTICATED_KEY) and replace both occurrences in GlobalProvider's effect with that constant; update any imports/exports as appropriate so the constant is reusable within GlobalProvider (or nearby constants file) to prevent typos and improve maintainability when reading/modifying the logic that sets/removes the key and calls Utils.CartStorage.removeAllCartIds().packages/utils/frontend/src/utils/cart-storage.ts (1)
31-42: Consider extracting the key resolution logic to reduce duplication.The pattern
orgId ?? _currentOrgId ?? DEFAULT_KEYis repeated ingetCartId,setCartId, andremoveCartId. Extracting this to a helper function would improve maintainability and ensure consistent behavior.♻️ Suggested refactor
+function resolveOrgKey(orgId?: string): string { + return orgId ?? _currentOrgId ?? DEFAULT_KEY; +} + export function getCartId(orgId?: string): string | null { const raw = localStorage.getItem(_storageKey); if (!raw) return null; try { const map: CartStorageMap = JSON.parse(raw); - const key = orgId ?? _currentOrgId ?? DEFAULT_KEY; + const key = resolveOrgKey(orgId); return map[key] ?? null; } catch { return null; } } export function setCartId(cartId: string, orgId?: string): void { - const key = orgId ?? _currentOrgId ?? DEFAULT_KEY; + const key = resolveOrgKey(orgId); const map = readMap(); map[key] = cartId; localStorage.setItem(_storageKey, JSON.stringify(map)); } export function removeCartId(orgId?: string): void { - const key = orgId ?? _currentOrgId ?? DEFAULT_KEY; + const key = resolveOrgKey(orgId); const map = readMap(); delete map[key]; // ... }Also applies to: 47-52, 57-67
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/utils/frontend/src/utils/cart-storage.ts` around lines 31 - 42, Extract the repeated key-resolution logic (orgId ?? _currentOrgId ?? DEFAULT_KEY) into a small helper like resolveCartKey(orgId?: string): string that returns the resolved storage key, then replace the inline expression in getCartId, setCartId, and removeCartId with calls to resolveCartKey so all three functions use the single helper; ensure the helper uses the same DEFAULT_KEY and _currentOrgId symbols and keeps types compatible with CartStorageMap lookups and assignments.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/frontend/src/app/`[locale]/not-found.tsx:
- Line 70: The not-found route now mounts GlobalProvider without the configured
cart storage props because Header no longer passes them; update the
not-found.tsx to ensure cart storage is initialized by supplying the same user
and cartStorageKey (or equivalent storage init values) to GlobalProvider (the
component instance that wraps the page) or explicitly initialize cart storage
before rendering, referencing the Header component removal and the
GlobalProvider symbol so the provider receives the correct user/cartStorageKey
used elsewhere in the app.
In
`@packages/blocks/checkout/checkout-summary/src/frontend/CheckoutSummary.client.tsx`:
- Line 47: CartStorage is never initialized so Utils.CartStorage.getCartId() and
removeCartId() in CheckoutSummary.client.tsx always fall back to the guest key;
to fix, either ensure configureCartStorage(...) runs before any CartStorage
usage (e.g., call configureCartStorage(orgId) from the app root/layout or
instantiate GlobalProvider early) or change the calls in
CheckoutSummary.client.tsx to pass the orgId explicitly (use
Utils.CartStorage.getCartId(orgId) and Utils.CartStorage.removeCartId(orgId));
locate the usages in CheckoutSummary.client.tsx (the getCartId and removeCartId
calls) and update them to one of these two approaches so _currentOrgId is set
and org-scoped carts are preserved.
In `@packages/utils/frontend/src/utils/cart-storage.ts`:
- Around line 15-19: The JSDoc for configureCartStorage incorrectly says the
orgId fallback is '__default__' while the code uses the DEFAULT_KEY constant set
to 'guest'; make them consistent by updating the JSDoc to state that the default
orgId is 'guest' (or, if intended behavior was '__default__', change DEFAULT_KEY
to '__default__' and update any references). Ensure the comment for the
config.orgId parameter and any examples reference the same value as the
DEFAULT_KEY constant.
---
Nitpick comments:
In `@packages/ui/src/providers/GlobalProvider/GlobalProvider.tsx`:
- Around line 81-86: Extract the hardcoded sessionStorage key 'wasAuthenticated'
into a descriptive constant (e.g., WAS_AUTHENTICATED_KEY) and replace both
occurrences in GlobalProvider's effect with that constant; update any
imports/exports as appropriate so the constant is reusable within GlobalProvider
(or nearby constants file) to prevent typos and improve maintainability when
reading/modifying the logic that sets/removes the key and calls
Utils.CartStorage.removeAllCartIds().
In `@packages/utils/frontend/src/utils/cart-storage.ts`:
- Around line 31-42: Extract the repeated key-resolution logic (orgId ??
_currentOrgId ?? DEFAULT_KEY) into a small helper like resolveCartKey(orgId?:
string): string that returns the resolved storage key, then replace the inline
expression in getCartId, setCartId, and removeCartId with calls to
resolveCartKey so all three functions use the single helper; ensure the helper
uses the same DEFAULT_KEY and _currentOrgId symbols and keeps types compatible
with CartStorageMap lookups and assignments.
🪄 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: 72e82aff-03bc-4c45-b4c8-81c973d476c0
📒 Files selected for processing (44)
.changeset/clever-ravens-follow.md.storybook/preview.tsxapps/frontend/src/app/[locale]/(auth)/login/page.tsxapps/frontend/src/app/[locale]/[[...slug]]/page.tsxapps/frontend/src/app/[locale]/not-found.tsxapps/frontend/src/containers/Header/CartInfo/CartInfo.tsxapps/frontend/src/containers/Header/CartInfo/CartInfo.types.tsapps/frontend/src/containers/Header/Header.tsxapps/frontend/src/containers/Header/Header.types.tspackages/blocks/checkout/cart/src/frontend/Cart.client.stories.tsxpackages/blocks/checkout/cart/src/frontend/Cart.client.tsxpackages/blocks/checkout/cart/src/frontend/Cart.server.tsxpackages/blocks/checkout/cart/src/frontend/Cart.types.tspackages/blocks/checkout/checkout-billing-payment/src/frontend/CheckoutBillingPayment.client.stories.tsxpackages/blocks/checkout/checkout-billing-payment/src/frontend/CheckoutBillingPayment.client.tsxpackages/blocks/checkout/checkout-billing-payment/src/frontend/CheckoutBillingPayment.server.tsxpackages/blocks/checkout/checkout-billing-payment/src/frontend/CheckoutBillingPayment.types.tspackages/blocks/checkout/checkout-company-data/src/frontend/CheckoutCompanyData.client.stories.tsxpackages/blocks/checkout/checkout-company-data/src/frontend/CheckoutCompanyData.client.tsxpackages/blocks/checkout/checkout-company-data/src/frontend/CheckoutCompanyData.server.tsxpackages/blocks/checkout/checkout-company-data/src/frontend/CheckoutCompanyData.types.tspackages/blocks/checkout/checkout-shipping-address/src/frontend/CheckoutShippingAddress.client.stories.tsxpackages/blocks/checkout/checkout-shipping-address/src/frontend/CheckoutShippingAddress.client.tsxpackages/blocks/checkout/checkout-shipping-address/src/frontend/CheckoutShippingAddress.server.tsxpackages/blocks/checkout/checkout-shipping-address/src/frontend/CheckoutShippingAddress.types.tspackages/blocks/checkout/checkout-summary/src/frontend/CheckoutSummary.client.stories.tsxpackages/blocks/checkout/checkout-summary/src/frontend/CheckoutSummary.client.tsxpackages/blocks/checkout/checkout-summary/src/frontend/CheckoutSummary.server.tsxpackages/blocks/checkout/checkout-summary/src/frontend/CheckoutSummary.types.tspackages/blocks/products/product-details/src/frontend/ProductDetails.client.stories.tsxpackages/blocks/products/product-details/src/frontend/ProductDetails.client.tsxpackages/blocks/products/product-details/src/frontend/ProductDetails.server.tsxpackages/blocks/products/product-details/src/frontend/ProductDetails.types.tspackages/blocks/products/product-list/src/frontend/ProductList.client.stories.tsxpackages/blocks/products/product-list/src/frontend/ProductList.client.tsxpackages/blocks/products/product-list/src/frontend/ProductList.server.tsxpackages/blocks/products/product-list/src/frontend/ProductList.types.tspackages/blocks/products/recommended-products/src/frontend/RecommendedProducts.client.stories.tsxpackages/blocks/products/recommended-products/src/frontend/RecommendedProducts.client.tsxpackages/blocks/products/recommended-products/src/frontend/RecommendedProducts.server.tsxpackages/blocks/products/recommended-products/src/frontend/RecommendedProducts.types.tspackages/ui/src/providers/GlobalProvider/GlobalProvider.tsxpackages/utils/frontend/src/utils/cart-storage.tspackages/utils/frontend/src/utils/index.ts
💤 Files with no reviewable changes (12)
- apps/frontend/src/containers/Header/Header.types.ts
- packages/blocks/checkout/checkout-billing-payment/src/frontend/CheckoutBillingPayment.client.stories.tsx
- packages/blocks/checkout/cart/src/frontend/Cart.server.tsx
- packages/blocks/checkout/checkout-summary/src/frontend/CheckoutSummary.client.stories.tsx
- apps/frontend/src/containers/Header/CartInfo/CartInfo.types.ts
- packages/blocks/checkout/checkout-shipping-address/src/frontend/CheckoutShippingAddress.client.stories.tsx
- packages/blocks/checkout/checkout-company-data/src/frontend/CheckoutCompanyData.client.stories.tsx
- packages/blocks/products/product-details/src/frontend/ProductDetails.client.stories.tsx
- packages/blocks/products/product-details/src/frontend/ProductDetails.server.tsx
- packages/blocks/products/recommended-products/src/frontend/RecommendedProducts.server.tsx
- packages/blocks/products/product-list/src/frontend/ProductList.client.stories.tsx
- packages/blocks/products/recommended-products/src/frontend/RecommendedProducts.client.stories.tsx
| <GlobalProvider config={init} labels={init.labels} locale={locale} themes={init.themes}> | ||
| <div className="flex flex-col min-h-dvh"> | ||
| <Header data={init.common.header} cartIdLocalStorageKey={process.env.CART_ID_LOCAL_STORAGE_KEY} /> | ||
| <Header data={init.common.header} /> |
There was a problem hiding this comment.
Cart storage is no longer initialized on this route.
After removing the Header prop at Line 70, this page still mounts GlobalProvider without user/cartStorageKey, so cart reads can fall back to defaults instead of the configured storage key.
💡 Suggested fix
- <GlobalProvider config={init} labels={init.labels} locale={locale} themes={init.themes}>
+ <GlobalProvider
+ config={init}
+ labels={init.labels}
+ locale={locale}
+ themes={init.themes}
+ user={{ orgId: session?.user?.customer?.id }}
+ cartStorageKey={process.env.CART_ID_LOCAL_STORAGE_KEY}
+ >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/frontend/src/app/`[locale]/not-found.tsx at line 70, The not-found route
now mounts GlobalProvider without the configured cart storage props because
Header no longer passes them; update the not-found.tsx to ensure cart storage is
initialized by supplying the same user and cartStorageKey (or equivalent storage
init values) to GlobalProvider (the component instance that wraps the page) or
explicitly initialize cart storage before rendering, referencing the Header
component removal and the GlobalProvider symbol so the provider receives the
correct user/cartStorageKey used elsewhere in the app.
|
|
||
| useEffect(() => { | ||
| const cartId = localStorage.getItem(cartIdLocalStorageKey); | ||
| const cartId = Utils.CartStorage.getCartId(); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Verify CartStorage configuration timing (GlobalProvider):"
rg -n --type=tsx -C4 'useEffect\(\(\) => \{' packages/ui/src/providers/GlobalProvider/GlobalProvider.tsx
rg -n --type=tsx -C4 'CartStorage\.configureCartStorage\(' packages/ui/src/providers/GlobalProvider/GlobalProvider.tsx
echo
echo "Verify CheckoutSummary cart access points:"
rg -n --type=tsx -C3 'CartStorage\.getCartId\(|CartStorage\.removeCartId\(' \
packages/blocks/checkout/checkout-summary/src/frontend/CheckoutSummary.client.tsx
echo
echo "Expected verification result:"
echo "- configureCartStorage is called in an effect (post-mount)"
echo "- CheckoutSummary reads cartId in its own effect/handlers"
echo "If confirmed, initialize CartStorage before descendant effects (or pass explicit orgId to CartStorage calls)."Repository: o2sdev/openselfservice
Length of output: 168
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Searching GlobalProvider for CartStorage configuration ==="
find . -name "GlobalProvider.tsx" -type f 2>/dev/null | head -5
echo
echo "=== Searching CheckoutSummary.client.tsx for CartStorage usage ==="
find . -name "CheckoutSummary.client.tsx" -type f 2>/dev/null | head -5
echo
echo "=== Looking for CartStorage configuration in GlobalProvider ==="
rg -n 'configureCartStorage|CartStorage' packages/ui/src/providers/GlobalProvider/ 2>/dev/null || echo "Path not found, searching entire repo..."
rg -n 'configureCartStorage|CartStorage' --type ts --type tsx 2>/dev/null | grep -i globalprovider | head -20
echo
echo "=== Looking for CartStorage usage in CheckoutSummary ==="
rg -n 'CartStorage\.(getCartId|removeCartId)' packages/blocks/checkout/checkout-summary/src/frontend/ 2>/dev/null || echo "Pattern search in path failed, trying broader search..."
rg -n 'CartStorage' packages/blocks/checkout/checkout-summary/src/frontend/CheckoutSummary.client.tsx 2>/dev/null || echo "File not found"Repository: o2sdev/openselfservice
Length of output: 683
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== GlobalProvider.tsx context around line 79 (CartStorage config) ==="
sed -n '70,95p' packages/ui/src/providers/GlobalProvider/GlobalProvider.tsx
echo
echo "=== CheckoutSummary.client.tsx full content (check lines 47, 72, 86) ==="
wc -l packages/blocks/checkout/checkout-summary/src/frontend/CheckoutSummary.client.tsx
cat -n packages/blocks/checkout/checkout-summary/src/frontend/CheckoutSummary.client.tsxRepository: o2sdev/openselfservice
Length of output: 22050
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Looking for CartStorage implementation ==="
find . -name "*CartStorage*" -type f 2>/dev/null | grep -v node_modules | head -10
echo
echo "=== Searching for CartStorage class/object definition ==="
rg -n "CartStorage\s*[=:]|class CartStorage|export.*CartStorage" --type ts --type tsx --max-count 20 2>/dev/null | head -30Repository: o2sdev/openselfservice
Length of output: 112
🏁 Script executed:
#!/bin/bash
set -euo pipeflag
echo "=== Searching for where CheckoutSummary is imported/used ==="
rg -n "CheckoutSummary|GlobalProvider" --type ts --type tsx packages/ 2>/dev/null | grep -E "import|export|Provider" | head -30
echo
echo "=== Looking for main layout or root setup files ==="
find packages -name "layout.tsx" -o -name "RootLayout.tsx" -o -name "page.tsx" -o -name "*Layout*" -type f 2>/dev/null | head -15
echo
echo "=== Checking if CheckoutSummary is wrapped by GlobalProvider ==="
rg -B5 "CheckoutSummary" packages/blocks/checkout/checkout-summary/ --type ts --type tsx 2>/dev/null | head -40Repository: o2sdev/openselfservice
Length of output: 119
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Searching for where CheckoutSummary is imported/used ==="
rg -n "CheckoutSummary|GlobalProvider" --type ts --type tsx packages/ 2>/dev/null | grep -E "import|export|Provider" | head -30
echo
echo "=== Looking for main layout or root setup files ==="
find packages -name "layout.tsx" -o -name "RootLayout.tsx" -o -name "page.tsx" -o -name "*Layout*" -type f 2>/dev/null | head -15
echo
echo "=== Checking if CheckoutSummary is wrapped by GlobalProvider ==="
rg -B5 "CheckoutSummary" packages/blocks/checkout/checkout-summary/ --type ts --type tsx 2>/dev/null | head -40Repository: o2sdev/openselfservice
Length of output: 126
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Find files that import CheckoutSummary (all file types) ==="
rg "import.*CheckoutSummary|from.*CheckoutSummary" --max-count 20 2>/dev/null | head -20
echo
echo "=== Look at the checkout-summary package structure ==="
find packages/blocks/checkout/checkout-summary -type f -name "*.tsx" -o -name "*.ts" -o -name "package.json" 2>/dev/null | sort
echo
echo "=== Check where CheckoutSummaryPure is exported ==="
rg "export.*CheckoutSummary" packages/blocks/checkout/checkout-summary/ 2>/dev/nullRepository: o2sdev/openselfservice
Length of output: 8008
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== CheckoutSummary.server.tsx content ==="
cat -n packages/blocks/checkout/checkout-summary/src/frontend/CheckoutSummary.server.tsx
echo
echo "=== CheckoutSummary.renderer.tsx content ==="
cat -n packages/blocks/checkout/checkout-summary/src/frontend/CheckoutSummary.renderer.tsx
echo
echo "=== Search for GlobalProvider usage around checkout pages ==="
rg -n "GlobalProvider" packages/ 2>/dev/null | head -15Repository: o2sdev/openselfservice
Length of output: 4489
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Search for app layout or root layout that uses GlobalProvider ==="
find . -path "*/node_modules" -prune -o -name "layout.tsx" -type f -print 2>/dev/null | head -10
echo
echo "=== Check app or root layout content for GlobalProvider ==="
rg -l "GlobalProvider" . --type ts --type tsx 2>/dev/null | grep -E "layout|app|root" | head -5
echo
echo "=== Look for any GlobalProvider usage in actual app setup ==="
rg -B3 -A3 "GlobalProvider" packages/*/src --type tsx --type ts 2>/dev/null | grep -v ".stories\|README" | head -50Repository: o2sdev/openselfservice
Length of output: 275
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Root layout ==="
cat -n ./apps/frontend/src/app/layout.tsx
echo
echo "=== Locale layout ==="
cat -n ./apps/frontend/src/app/[locale]/layout.tsxRepository: o2sdev/openselfservice
Length of output: 3197
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Search for where GlobalProvider is actually instantiated in the app ==="
rg -n "GlobalProvider" apps/ --type ts --type tsx 2>/dev/null
echo
echo "=== Check if there are other layout files or wrappers ==="
find apps/frontend/src -name "*.tsx" -o -name "*.ts" | xargs grep -l "GlobalProvider" 2>/dev/null
echo
echo "=== Look at the full GlobalProvider implementation again ==="
sed -n '64,100p' packages/ui/src/providers/GlobalProvider/GlobalProvider.tsxRepository: o2sdev/openselfservice
Length of output: 141
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Search for ALL CartStorage calls in the codebase ==="
rg -n "CartStorage\." packages/ --type ts --type tsx 2>/dev/null | grep -v ".stories\|node_modules" | head -30
echo
echo "=== Search for configureCartStorage calls specifically ==="
rg -n "configureCartStorage" . --type ts --type tsx 2>/dev/null | grep -v node_modules
echo
echo "=== Check CheckoutSummary props and where user/config comes from ==="
cat -n packages/blocks/checkout/checkout-summary/src/frontend/CheckoutSummary.types.tsRepository: o2sdev/openselfservice
Length of output: 122
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Search for CartStorage in all files ==="
rg "CartStorage" . 2>/dev/null | grep -v "node_modules" | head -40
echo
echo "=== Check CheckoutSummary types file ==="
cat -n packages/blocks/checkout/checkout-summary/src/frontend/CheckoutSummary.types.ts
echo
echo "=== Look for user/orgId context in CheckoutSummary ==="
rg -n "user\|orgId\|org" packages/blocks/checkout/checkout-summary/src/frontend/CheckoutSummary.client.tsx | head -20Repository: o2sdev/openselfservice
Length of output: 4438
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Read cart-storage.ts implementation ==="
cat -n packages/utils/frontend/src/utils/cart-storage.ts
echo
echo "=== Check if there's initialization elsewhere in product blocks ==="
rg -B5 "configureCartStorage" packages/blocks/ --type ts --type tsx 2>/dev/nullRepository: o2sdev/openselfservice
Length of output: 2906
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Final verification: All layout files in apps/frontend ==="
find apps/frontend/src -name "layout.tsx" -o -name "layout.ts" 2>/dev/null | xargs cat -n 2>/dev/null
echo
echo "=== Check if GlobalProvider is used anywhere in CheckoutSummary flow ==="
rg -n "GlobalProvider\|useGlobalContext" packages/blocks/checkout/ --type ts --type tsx 2>/dev/null
echo
echo "=== Where is user/orgId coming from in CheckoutSummary? ==="
grep -r "CheckoutSummaryPure\|CheckoutSummary" apps/ --include="*.tsx" --include="*.ts" 2>/dev/null | head -10Repository: o2sdev/openselfservice
Length of output: 3290
CartStorage is never initialized, causing all cart operations to use the fallback 'guest' org bucket.
CartStorage.getCartId() and CartStorage.removeCartId() at lines 47, 72, and 86 will always default to the 'guest' organization key because configureCartStorage() is never called in the application. GlobalProvider (which calls configureCartStorage) is not instantiated in the app layout, so _currentOrgId remains undefined and falls back to DEFAULT_KEY. This causes org-scoped carts to be lost/inaccessible.
Either initialize CartStorage before the app renders (add to root layout), or pass orgId explicitly to CartStorage methods: CartStorage.getCartId(orgId) and CartStorage.removeCartId(orgId).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@packages/blocks/checkout/checkout-summary/src/frontend/CheckoutSummary.client.tsx`
at line 47, CartStorage is never initialized so Utils.CartStorage.getCartId()
and removeCartId() in CheckoutSummary.client.tsx always fall back to the guest
key; to fix, either ensure configureCartStorage(...) runs before any CartStorage
usage (e.g., call configureCartStorage(orgId) from the app root/layout or
instantiate GlobalProvider early) or change the calls in
CheckoutSummary.client.tsx to pass the orgId explicitly (use
Utils.CartStorage.getCartId(orgId) and Utils.CartStorage.removeCartId(orgId));
locate the usages in CheckoutSummary.client.tsx (the getCartId and removeCartId
calls) and update them to one of these two approaches so _currentOrgId is set
and org-scoped carts are preserved.
There was a problem hiding this comment.
GlobalProvider (which wraps all pages) calls configureCartStorage() in a useEffect on mount — this runs before any child block component mounts. _currentOrgId is already set by the time getCartId()/removeCartId() are called.
There was a problem hiding this comment.
@lukasz-hycom, thank you for the clarification. There is an important nuance here worth flagging, though.
React fires useEffect hooks children-first, parents-last. This is well-established React behavior:
When a Parent and Child both mount in the same render, the order is: Child committed effect fires before Parent committed effect.
Effects are a different story from rendering. React effectively queues effects from the bottom of the tree upward, so Child's effects are called before Parent's.
This means that if GlobalProvider and CheckoutSummary mount in the same render cycle (e.g., a user lands directly on the checkout summary page for the first time), CheckoutSummaryPure's useEffect (which calls getCartId()) would fire before GlobalProvider's useEffect (which calls configureCartStorage()). At that point, _currentOrgId would not yet be set and the call would fall back to 'guest'.
Where your reasoning holds: If GlobalProvider lives in a persistent Next.js layout (one that stays mounted across client-side navigations), then on any navigation to the checkout page after the initial app load, GlobalProvider is already mounted and configureCartStorage() has already run. In that case, the ordering is fine.
The risk: If a user deep-links directly to the checkout summary URL (e.g., after a redirect from a payment provider), GlobalProvider and CheckoutSummary mount together in the same render, and the child's effect fires first — meaning getCartId() is called before configureCartStorage() sets _currentOrgId. For an authenticated user with an orgId, this would look up the cart under the 'guest' key and return null, triggering the "cart not found" toast and redirect.
Could you confirm whether GlobalProvider is placed in a persistent layout (e.g., apps/frontend/src/app/[locale]/layout.tsx) or co-rendered within the same page component as CheckoutSummary? That distinction determines whether this is a real risk.
✏️ Learnings added
Learnt from: lukasz-hycom
Repo: o2sdev/openselfservice PR: 840
File: packages/blocks/checkout/checkout-summary/src/frontend/CheckoutSummary.client.tsx:47-47
Timestamp: 2026-03-27T12:52:02.496Z
Learning: In the o2sdev/openselfservice repository, React's useEffect fires children-first, parents-last. GlobalProvider calls configureCartStorage() in a useEffect to set _currentOrgId for CartStorage. If GlobalProvider is co-rendered on the same page as a block like CheckoutSummary (not in a persistent Next.js layout), CheckoutSummary's useEffect (calling Utils.CartStorage.getCartId()) fires before GlobalProvider's configureCartStorage(), causing _currentOrgId to be unset and falling back to the 'guest' key on initial page load.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
Key Changes
Summary by CodeRabbit
New Features
Refactor