Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds affiliate analytics types and centralized tracking, instruments affiliate UI and hooks with de-duplicated view/event reporting, refactors modal/saved-code atoms and storage migration, enhances verification/create hooks to include codeSource and richer params, and adds many tests across atoms, hooks, and components. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant UI as Component
participant Analytics as AnalyticsClient
participant Storage as LocalStorage/Atoms
rect rgba(200,200,255,0.5)
User->>UI: click/copy/open modal/submit
end
rect rgba(200,255,200,0.5)
UI->>Analytics: trackAffiliateEvent(action, params)
Analytics->>Analytics: compact payload (remove undefined)
Analytics->>Analytics: sendEvent(category: 'Affiliate', action, params)
end
rect rgba(255,230,200,0.5)
UI->>Storage: update atoms / persist saved code (with source)
Storage-->>UI: state updated / ack
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 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 |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeCreate.ts (1)
42-90:⚠️ Potential issue | 🟠 MajorGuard
onCreateagainst double submission.This callback can be entered twice before the button disables, which means duplicate signature prompts and concurrent
createCode(...)writes. Because this is a create flow, it should have an in-flight guard inside the hook, not only in the UI.Suggested fix
-import { useCallback, useMemo, useState } from 'react' +import { useCallback, useMemo, useRef, useState } from 'react' ... export function useAffiliatePartnerCodeCreate({ account, provider, code, setError, }: UseAffiliatePartnerCodeCreateParams): UseAffiliatePartnerCodeCreateResult { const analytics = useCowAnalytics() + const submittingRef = useRef(false) const [submitting, setSubmitting] = useState(false) const { mutate: mutatePartnerInfo } = useAffiliatePartnerInfo(account) const onCreate = useCallback(async (): Promise<void> => { - if (!account || !provider) return + if (!account || !provider || submittingRef.current) return + + submittingRef.current = true trackAffiliateEvent({ analytics, action: 'affiliate_partner_code_create_started', chainId: AFFILIATE_PAYOUTS_CHAIN_ID, @@ } finally { + submittingRef.current = false setSubmitting(false) } - }, [account, analytics, code, mutatePartnerInfo, provider, setError]) + }, [account, analytics, code, mutatePartnerInfo, provider, setError])🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeCreate.ts` around lines 42 - 90, The onCreate callback can run concurrently and must guard against double submission: add an in-flight guard at the top of onCreate that returns early if a submission is already in progress (use the existing submitting state or, better, a mutable ref like isSubmittingRef to avoid stale closure issues), set the guard true immediately before starting the try block, and ensure it is reset to false in the finally block; keep the rest of the flow (provider.getSigner(), signer._signTypedData, bffAffiliateApi.createCode, trackAffiliateEvent, mutatePartnerInfo, setError) unchanged and reference onCreate, setSubmitting, submitting (or isSubmittingRef) and mutatePartnerInfo when making the change.
🧹 Nitpick comments (3)
apps/cowswap-frontend/src/modules/affiliate/containers/AffiliateTraderCodeInfo.tsx (1)
123-123: Extract the inline click handler for modal open.Line 123 introduces a render-time inline factory. Prefer a stable callback for consistency with render-stability rules.
♻️ Suggested refactor
-import { ReactNode, useMemo } from 'react' +import { ReactNode, useCallback, useMemo } from 'react' @@ const openAffiliateModal = useSetAtom(openTraderModalAtom) + const onEditCodeClick = useCallback((): void => { + openAffiliateModal(AffiliateEntrySource.TRADER_PAGE_CODE_CARD) + }, [openAffiliateModal]) @@ - <ButtonPrimary onClick={() => openAffiliateModal(AffiliateEntrySource.TRADER_PAGE_CODE_CARD)}> + <ButtonPrimary onClick={onEditCodeClick}>As per coding guidelines:
Replace inline factories with extracted components or memoized callbacks; obey react/no-unstable-nested-components.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/cowswap-frontend/src/modules/affiliate/containers/AffiliateTraderCodeInfo.tsx` at line 123, The inline click handler passed to ButtonPrimary in AffiliateTraderCodeInfo should be extracted into a stable callback to avoid render-time inline factories; create a memoized handler (e.g., const handleOpenAffiliateModal = useCallback(() => openAffiliateModal(AffiliateEntrySource.TRADER_PAGE_CODE_CARD), [openAffiliateModal])) inside the AffiliateTraderCodeInfo component and pass it to ButtonPrimary via onClick={handleOpenAffiliateModal}; ensure React's useCallback is imported and that AffiliateEntrySource.TRADER_PAGE_CODE_CARD and openAffiliateModal identifiers are used exactly as in the current file.apps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeAvailability.ts (1)
48-59: Consider migrating from SWR toatomWithQuery.Per coding guidelines, SWR is deprecated for new data fetching. This existing SWR flow could be migrated to Jotai's
atomWithQueryin a follow-up.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeAvailability.ts` around lines 48 - 59, Replace the SWR hook in useAffiliatePartnerCodeAvailability.ts with a Jotai atom created via atomWithQuery: remove useSWR call that defines data:isAvailable, error, isLoading and instead create an atom that takes debouncedCode and waitingForDebouncedInput as inputs and uses bffAffiliateApi.verifyCodeAvailability(code) as the fetcher; then expose derived values (isAvailable, error, isLoading) from that atom and drop SWR_NO_REFRESH_OPTIONS and shouldRetryOnError. Ensure the new atom key/inputs mirror the previous conditional (null when waitingForDebouncedInput or no debouncedCode) so no request is made, and reference the existing symbols debouncedCode, waitingForDebouncedInput, and bffAffiliateApi.verifyCodeAvailability to locate where to replace the useSWR logic.apps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliateTraderCodeInput.ts (1)
103-109: Dependency array foronChangemay be incomplete.
setCodeSourceandshouldAutoVerifyare used inresetTraderCodeInputStatebut not listed in deps. SincesetCodeSourceis a useState setter (stable) andshouldAutoVerifyis a ref (stable identity), this works correctly, but including them would be more explicit.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliateTraderCodeInput.ts` around lines 103 - 109, The onChange callback’s dependency array is incomplete: it calls resetTraderCodeInputState and uses setCodeSource and shouldAutoVerify but only lists setError; update the useCallback dependencies for onChange to include resetTraderCodeInputState, setCodeSource, and shouldAutoVerify (in addition to setError) so the hook is explicit about all referenced symbols (even though setCodeSource and shouldAutoVerify are stable).
🤖 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/cowswap-frontend/src/modules/affiliate/analytics/affiliateAnalytics.utils.ts`:
- Around line 62-81: The function getAffiliateTraderPageState currently sets
LINKED only when hasSavedCode is true, causing a walletStatus of
TraderWalletStatus.LINKED to be misclassified as ONBOARD if hasSavedCode is
false; update getAffiliateTraderPageState to explicitly check for
TraderWalletStatus.LINKED and return AffiliatePageState.LINKED (independent of
hasSavedCode) before the final default/other switches so the page-state
correctly reflects the wallet's LINKED status.
In
`@apps/cowswap-frontend/src/modules/affiliate/containers/AffiliateTraderModalCodeLinking.tsx`:
- Around line 66-90: onSubmit currently tracks clicks for the savedCode and
connect-to-verify branches but does not emit an analytics event before calling
verifyCode when the user is connected and has no saved code; add a
trackAffiliateEvent call (using the same analytics, entrySource, walletStatus
props) right before the void verifyCode(...) call with action:
'affiliate_trader_modal_primary_cta_clicked' and ctaType: 'verifyCode' so the
verify path is instrumented; update the onSubmit flow to call
trackAffiliateEvent(...), then call verifyCode({ code: codeInput, account,
codeSource }).
In
`@apps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliateStateViewAnalytics.ts`:
- Around line 22-39: The hook useAffiliateStateViewAnalytics currently
deduplicates emissions using only lastViewKeyRef/current viewKey, causing
updates to eventParams to be ignored; change the dedupe to include the full
analytics signature (e.g., combine viewKey and eventParams or accept a
dedupeKey) so that when eventParams changes the effect re-triggers: update
lastViewKeyRef to store a composite key (or add lastPayloadRef) and modify the
equality check and dependencies (the effect's dependency array and compare logic
surrounding lastViewKeyRef, viewKey, eventParams, analytics) so
trackAffiliateEvent is called whenever any payload-affecting field changes;
ensure trackAffiliateEvent call sites remain unchanged.
In
`@apps/cowswap-frontend/src/modules/affiliate/state/migrations/affiliateTraderSavedCodeStorage.utils.ts`:
- Around line 96-98: The isRecord type guard currently treats arrays as objects;
update isRecord to exclude arrays by adding an explicit check (e.g., use
!Array.isArray(value)) so it only returns true for plain object records; locate
the isRecord function and change its boolean expression to include the array
exclusion to prevent arrays from being treated as Record<string, unknown>.
In `@apps/cowswap-frontend/src/pages/Account/AffiliateTrader.tsx`:
- Line 33: The constant hasSavedCode is using Boolean(savedCode); update the
assignment to use explicit double-negation (two exclamation marks) on savedCode
instead to match project convention — i.e., replace the Boolean conversion with
the !! savedCode style where hasSavedCode is defined.
---
Outside diff comments:
In
`@apps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeCreate.ts`:
- Around line 42-90: The onCreate callback can run concurrently and must guard
against double submission: add an in-flight guard at the top of onCreate that
returns early if a submission is already in progress (use the existing
submitting state or, better, a mutable ref like isSubmittingRef to avoid stale
closure issues), set the guard true immediately before starting the try block,
and ensure it is reset to false in the finally block; keep the rest of the flow
(provider.getSigner(), signer._signTypedData, bffAffiliateApi.createCode,
trackAffiliateEvent, mutatePartnerInfo, setError) unchanged and reference
onCreate, setSubmitting, submitting (or isSubmittingRef) and mutatePartnerInfo
when making the change.
---
Nitpick comments:
In
`@apps/cowswap-frontend/src/modules/affiliate/containers/AffiliateTraderCodeInfo.tsx`:
- Line 123: The inline click handler passed to ButtonPrimary in
AffiliateTraderCodeInfo should be extracted into a stable callback to avoid
render-time inline factories; create a memoized handler (e.g., const
handleOpenAffiliateModal = useCallback(() =>
openAffiliateModal(AffiliateEntrySource.TRADER_PAGE_CODE_CARD),
[openAffiliateModal])) inside the AffiliateTraderCodeInfo component and pass it
to ButtonPrimary via onClick={handleOpenAffiliateModal}; ensure React's
useCallback is imported and that AffiliateEntrySource.TRADER_PAGE_CODE_CARD and
openAffiliateModal identifiers are used exactly as in the current file.
In
`@apps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeAvailability.ts`:
- Around line 48-59: Replace the SWR hook in
useAffiliatePartnerCodeAvailability.ts with a Jotai atom created via
atomWithQuery: remove useSWR call that defines data:isAvailable, error,
isLoading and instead create an atom that takes debouncedCode and
waitingForDebouncedInput as inputs and uses
bffAffiliateApi.verifyCodeAvailability(code) as the fetcher; then expose derived
values (isAvailable, error, isLoading) from that atom and drop
SWR_NO_REFRESH_OPTIONS and shouldRetryOnError. Ensure the new atom key/inputs
mirror the previous conditional (null when waitingForDebouncedInput or no
debouncedCode) so no request is made, and reference the existing symbols
debouncedCode, waitingForDebouncedInput, and
bffAffiliateApi.verifyCodeAvailability to locate where to replace the useSWR
logic.
In
`@apps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliateTraderCodeInput.ts`:
- Around line 103-109: The onChange callback’s dependency array is incomplete:
it calls resetTraderCodeInputState and uses setCodeSource and shouldAutoVerify
but only lists setError; update the useCallback dependencies for onChange to
include resetTraderCodeInputState, setCodeSource, and shouldAutoVerify (in
addition to setError) so the hook is explicit about all referenced symbols (even
though setCodeSource and shouldAutoVerify are stable).
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 95c0ae3f-d317-4af4-b8c4-b3fd63f99928
📒 Files selected for processing (34)
apps/cowswap-frontend/src/common/analytics/types.tsapps/cowswap-frontend/src/legacy/components/Copy/CopyMod.tsxapps/cowswap-frontend/src/modules/affiliate/analytics/affiliateAnalytics.test.tsapps/cowswap-frontend/src/modules/affiliate/analytics/affiliateAnalytics.types.tsapps/cowswap-frontend/src/modules/affiliate/analytics/affiliateAnalytics.utils.tsapps/cowswap-frontend/src/modules/affiliate/config/affiliateProgram.const.tsapps/cowswap-frontend/src/modules/affiliate/containers/AffiliatePartnerCodeCreation.tsxapps/cowswap-frontend/src/modules/affiliate/containers/AffiliatePartnerCodeInfo.tsxapps/cowswap-frontend/src/modules/affiliate/containers/AffiliatePartnerOnboard.tsxapps/cowswap-frontend/src/modules/affiliate/containers/AffiliatePartnerQrModal.tsxapps/cowswap-frontend/src/modules/affiliate/containers/AffiliateTraderCodeInfo.tsxapps/cowswap-frontend/src/modules/affiliate/containers/AffiliateTraderModal.tsxapps/cowswap-frontend/src/modules/affiliate/containers/AffiliateTraderModalCodeInfo.tsxapps/cowswap-frontend/src/modules/affiliate/containers/AffiliateTraderModalCodeLinking.tsxapps/cowswap-frontend/src/modules/affiliate/containers/AffiliateTraderModalIneligible.tsxapps/cowswap-frontend/src/modules/affiliate/containers/AffiliateTraderOnboard.tsxapps/cowswap-frontend/src/modules/affiliate/containers/AffiliateTraderRewardsRow.tsxapps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeAvailability.tsapps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeCreate.tsapps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliateStateViewAnalytics.tsapps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliateTraderCodeInput.tsapps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliateTraderRecoverySideEffect.tsapps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliateTraderRefUrlSideEffect.tsapps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliateTraderVerification.tsapps/cowswap-frontend/src/modules/affiliate/index.tsapps/cowswap-frontend/src/modules/affiliate/pure/AffiliatePartner/AffiliatePartnerCodeForm.tsxapps/cowswap-frontend/src/modules/affiliate/pure/HowItWorks.tsxapps/cowswap-frontend/src/modules/affiliate/state/affiliateTraderModalAtom.test.tsapps/cowswap-frontend/src/modules/affiliate/state/affiliateTraderModalAtom.tsapps/cowswap-frontend/src/modules/affiliate/state/affiliateTraderSavedCodeAtom.test.tsapps/cowswap-frontend/src/modules/affiliate/state/affiliateTraderSavedCodeAtom.tsapps/cowswap-frontend/src/modules/affiliate/state/migrations/affiliateTraderSavedCodeStorage.utils.tsapps/cowswap-frontend/src/pages/Account/AffiliatePartner.tsxapps/cowswap-frontend/src/pages/Account/AffiliateTrader.tsx
| export function getAffiliateTraderPageState( | ||
| walletStatus: TraderWalletStatus, | ||
| hasSavedCode: boolean, | ||
| ): AffiliatePageState { | ||
| if (hasSavedCode && walletStatus !== TraderWalletStatus.DISCONNECTED) { | ||
| return AffiliatePageState.LINKED | ||
| } | ||
|
|
||
| switch (walletStatus) { | ||
| case TraderWalletStatus.PENDING: | ||
| return AffiliatePageState.LOADING | ||
| case TraderWalletStatus.UNSUPPORTED: | ||
| return AffiliatePageState.UNSUPPORTED | ||
| case TraderWalletStatus.INELIGIBLE: | ||
| return AffiliatePageState.INELIGIBLE | ||
| case TraderWalletStatus.ELIGIBILITY_UNKNOWN: | ||
| return AffiliatePageState.ELIGIBILITY_UNKNOWN | ||
| default: | ||
| return AffiliatePageState.ONBOARD | ||
| } |
There was a problem hiding this comment.
Handle TraderWalletStatus.LINKED explicitly to avoid misclassified page-state analytics.
On Line 66, LINKED depends on hasSavedCode. If wallet status is already LINKED but local saved code is absent (e.g., before/while recovery), this falls through to ONBOARD, which emits incorrect funnel state.
💡 Suggested fix
export function getAffiliateTraderPageState(
walletStatus: TraderWalletStatus,
hasSavedCode: boolean,
): AffiliatePageState {
- if (hasSavedCode && walletStatus !== TraderWalletStatus.DISCONNECTED) {
+ if (walletStatus === TraderWalletStatus.LINKED) {
+ return AffiliatePageState.LINKED
+ }
+
+ if (hasSavedCode && walletStatus !== TraderWalletStatus.DISCONNECTED) {
return AffiliatePageState.LINKED
}
switch (walletStatus) {
case TraderWalletStatus.PENDING:
return AffiliatePageState.LOADING📝 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.
| export function getAffiliateTraderPageState( | |
| walletStatus: TraderWalletStatus, | |
| hasSavedCode: boolean, | |
| ): AffiliatePageState { | |
| if (hasSavedCode && walletStatus !== TraderWalletStatus.DISCONNECTED) { | |
| return AffiliatePageState.LINKED | |
| } | |
| switch (walletStatus) { | |
| case TraderWalletStatus.PENDING: | |
| return AffiliatePageState.LOADING | |
| case TraderWalletStatus.UNSUPPORTED: | |
| return AffiliatePageState.UNSUPPORTED | |
| case TraderWalletStatus.INELIGIBLE: | |
| return AffiliatePageState.INELIGIBLE | |
| case TraderWalletStatus.ELIGIBILITY_UNKNOWN: | |
| return AffiliatePageState.ELIGIBILITY_UNKNOWN | |
| default: | |
| return AffiliatePageState.ONBOARD | |
| } | |
| export function getAffiliateTraderPageState( | |
| walletStatus: TraderWalletStatus, | |
| hasSavedCode: boolean, | |
| ): AffiliatePageState { | |
| if (walletStatus === TraderWalletStatus.LINKED) { | |
| return AffiliatePageState.LINKED | |
| } | |
| if (hasSavedCode && walletStatus !== TraderWalletStatus.DISCONNECTED) { | |
| return AffiliatePageState.LINKED | |
| } | |
| switch (walletStatus) { | |
| case TraderWalletStatus.PENDING: | |
| return AffiliatePageState.LOADING | |
| case TraderWalletStatus.UNSUPPORTED: | |
| return AffiliatePageState.UNSUPPORTED | |
| case TraderWalletStatus.INELIGIBLE: | |
| return AffiliatePageState.INELIGIBLE | |
| case TraderWalletStatus.ELIGIBILITY_UNKNOWN: | |
| return AffiliatePageState.ELIGIBILITY_UNKNOWN | |
| default: | |
| return AffiliatePageState.ONBOARD | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@apps/cowswap-frontend/src/modules/affiliate/analytics/affiliateAnalytics.utils.ts`
around lines 62 - 81, The function getAffiliateTraderPageState currently sets
LINKED only when hasSavedCode is true, causing a walletStatus of
TraderWalletStatus.LINKED to be misclassified as ONBOARD if hasSavedCode is
false; update getAffiliateTraderPageState to explicitly check for
TraderWalletStatus.LINKED and return AffiliatePageState.LINKED (independent of
hasSavedCode) before the final default/other switches so the page-state
correctly reflects the wallet's LINKED status.
elena-zh
left a comment
There was a problem hiding this comment.
Hey @fairlighteth , looks good! Maybe 2 nitpicks:
- When I create an affiliate code, I see 2 events fired one after another: code creation completed, then another the same one with the reason 'code is unavailable'. Is it OK?
- I did not notice that there is an event for an expired code: should we add it? Or if it exists, let me know please a name of the event I should check.
Thanks!
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeCreate.ts (1)
46-57:⚠️ Potential issue | 🟠 MajorMove pre-submit side effects inside the
tryto guarantee guard cleanup.
isSubmittingRef.currentis set before enteringtry, but analytics/state calls run outside it. If one of those throws, cleanup never runs and submissions stay locked for this component lifetime.🔧 Suggested fix
const onCreate = useCallback(async (): Promise<void> => { if (!account || !provider || isSubmittingRef.current) return isSubmittingRef.current = true - - trackAffiliateEvent({ - analytics, - action: 'affiliate_partner_code_create_started', - chainId: AFFILIATE_PAYOUTS_CHAIN_ID, - }) - - setSubmitting(true) - setError(undefined) - try { + trackAffiliateEvent({ + analytics, + action: 'affiliate_partner_code_create_started', + chainId: AFFILIATE_PAYOUTS_CHAIN_ID, + }) + setSubmitting(true) + setError(undefined) + const signer = provider.getSigner() const typedData = buildPartnerTypedData({ walletAddress: account, code, chainId: AFFILIATE_PAYOUTS_CHAIN_ID,Also applies to: 90-92
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeCreate.ts` around lines 46 - 57, Move the pre-submit side effects (setting isSubmittingRef.current = true, calling trackAffiliateEvent, setSubmitting(true), setError(undefined)) into the try block inside useAffiliatePartnerCodeCreate so any exception thrown by analytics/state updates is caught and the finally cleanup runs; ensure the same rearrangement is applied to the duplicate block around the code referenced at lines 90-92, and keep the finally block that resets isSubmittingRef.current and setSubmitting(false) unchanged so submissions are never left locked.
🧹 Nitpick comments (1)
apps/cowswap-frontend/src/modules/affiliate/state/migrations/affiliateTraderSavedCodeStorage.utils.ts (1)
26-34: Add a removal TODO for the legacy migration.The migration from v0 to v1 storage should include a cleanup TODO with a target removal date. This helps track when the migration code can be safely removed after sufficient time has passed for users to migrate.
As per coding guidelines: "Tag migrations with a removal
// TODOdate for cleanup"📅 Proposed TODO annotation
const legacyValue = readSavedCodeState(AFFILIATE_TRADER_SAVED_CODES_LEGACY_STORAGE_KEY) if (!legacyValue) { return initialValue } + // TODO(2026-07-15): Remove v0 → v1 migration after 3 months localStorage.setItem(AFFILIATE_TRADER_SAVED_CODES_STORAGE_KEY, JSON.stringify(legacyValue)) localStorage.removeItem(AFFILIATE_TRADER_SAVED_CODES_LEGACY_STORAGE_KEY)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/cowswap-frontend/src/modules/affiliate/state/migrations/affiliateTraderSavedCodeStorage.utils.ts` around lines 26 - 34, Add a one-line TODO above the legacy migration that documents when the migration code can be removed (e.g., target removal date), so future cleanup is tracked; specifically, update the block that reads from readSavedCodeState(AFFILIATE_TRADER_SAVED_CODES_LEGACY_STORAGE_KEY) and writes to AFFILIATE_TRADER_SAVED_CODES_STORAGE_KEY to include a comment like "// TODO: remove migration after YYYY-MM" to mark the planned removal date for the legacy handling of AFFILIATE_TRADER_SAVED_CODES_LEGACY_STORAGE_KEY.
🤖 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/cowswap-frontend/src/modules/affiliate/containers/AffiliateTraderModalCodeLinking.tsx`:
- Around line 64-75: The onTogglePayoutConfirmed callback is sending analytics
without the modal context fields; update the trackAffiliateEvent call inside
onTogglePayoutConfirmed to include entrySource and walletStatus (or the
equivalent variables used elsewhere in this file) so the payout-confirmation
event is segmented consistently with the trader modal funnel, and add those
variables to the useCallback dependency array; locate the
onTogglePayoutConfirmed function and the trackAffiliateEvent invocation to apply
this change.
In
`@apps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeCreate.test.tsx`:
- Around line 40-41: The createDeferred helper currently declares let resolve!
and let reject! using non-null assertion; remove the definite-assignment
assertions and instead initialize resolve and reject with safe defaults that
throw if called before being set (e.g., functions that throw an explicit
"deferred not initialized" error), preserving their types (resolve: (value: T)
=> void, reject: (error?: unknown) => void) and then reassign them when the real
Promise executor runs; update any type annotations in createDeferred so no
non-null assertions are used.
In
`@apps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliateStateViewAnalytics.ts`:
- Line 10: The eventParams type and usage in useAffiliateStateViewAnalytics
currently allow reserved keys ('action' and 'analytics') to be passed and
overwrite analytics fields; change the param type to enforce those keys can't be
provided (e.g., eventParams?: Record<string, unknown> & { action?: never;
analytics?: never }) and before merging/spreading into the final payload
sanitize the runtime object (e.g., create sanitizedParams from eventParams and
delete sanitizedParams.action and sanitizedParams.analytics) so reserved fields
cannot be overridden when constructing the analytics event.
---
Outside diff comments:
In
`@apps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeCreate.ts`:
- Around line 46-57: Move the pre-submit side effects (setting
isSubmittingRef.current = true, calling trackAffiliateEvent,
setSubmitting(true), setError(undefined)) into the try block inside
useAffiliatePartnerCodeCreate so any exception thrown by analytics/state updates
is caught and the finally cleanup runs; ensure the same rearrangement is applied
to the duplicate block around the code referenced at lines 90-92, and keep the
finally block that resets isSubmittingRef.current and setSubmitting(false)
unchanged so submissions are never left locked.
---
Nitpick comments:
In
`@apps/cowswap-frontend/src/modules/affiliate/state/migrations/affiliateTraderSavedCodeStorage.utils.ts`:
- Around line 26-34: Add a one-line TODO above the legacy migration that
documents when the migration code can be removed (e.g., target removal date), so
future cleanup is tracked; specifically, update the block that reads from
readSavedCodeState(AFFILIATE_TRADER_SAVED_CODES_LEGACY_STORAGE_KEY) and writes
to AFFILIATE_TRADER_SAVED_CODES_STORAGE_KEY to include a comment like "// TODO:
remove migration after YYYY-MM" to mark the planned removal date for the legacy
handling of AFFILIATE_TRADER_SAVED_CODES_LEGACY_STORAGE_KEY.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: eec813d1-bf48-492a-80be-dcf1d99ac397
📒 Files selected for processing (7)
apps/cowswap-frontend/src/modules/affiliate/analytics/affiliateAnalytics.test.tsapps/cowswap-frontend/src/modules/affiliate/containers/AffiliateTraderModalCodeLinking.tsxapps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeCreate.test.tsxapps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeCreate.tsapps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliateStateViewAnalytics.tsapps/cowswap-frontend/src/modules/affiliate/state/migrations/affiliateTraderSavedCodeStorage.utils.tsapps/cowswap-frontend/src/pages/Account/AffiliateTrader.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/cowswap-frontend/src/pages/Account/AffiliateTrader.tsx
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
apps/cowswap-frontend/src/modules/affiliate/containers/AffiliateTraderExpiryBanner.test.tsx (1)
117-131: Add a boundary test forrewards_end === now.Current tests cover before/after; adding exact-equality coverage would protect against expiry-state edge regressions in both banner rendering and analytics payload selection.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/cowswap-frontend/src/modules/affiliate/containers/AffiliateTraderExpiryBanner.test.tsx` around lines 117 - 131, Add a new unit test in AffiliateTraderExpiryBanner.test.tsx that mocks useAffiliateTraderStatsMock (via createTraderStatsResponse) with rewards_end exactly equal to the mocked machine time from useMachineTimeMsMock (Date.parse(...)), calls renderComponent(), and asserts the banner/analytics treat this as the expiry boundary: specifically verify useAffiliateStateViewAnalyticsMock is invoked with the expired payload (action: 'affiliate_trader_expired_code_viewed') and that the component renders the expired-banner state accordingly; locate mocks and helpers by names useAffiliateTraderStatsMock, createTraderStatsResponse, useMachineTimeMsMock, renderComponent, and useAffiliateStateViewAnalyticsMock.apps/cowswap-frontend/src/modules/affiliate/hooks/useIsRefCodeExpired.test.tsx (1)
91-102: Test intent and assertions are slightly misaligned.The title says “tracking is enabled”, but this case only asserts expiry state. Consider either renaming the test or asserting tracking behavior explicitly.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/cowswap-frontend/src/modules/affiliate/hooks/useIsRefCodeExpired.test.tsx` around lines 91 - 102, The test title and assertions are misaligned; either make the title reflect that this test only asserts expiry (e.g., rename the it(...) string to "still reports expired codes") or add an explicit assertion that verifies tracking is enabled (ensure useAffiliateTraderStatsMock is called with the tracking flag true). Locate the test in useIsRefCodeExpired.test.tsx and update the it(...) description or add/check the assertion that useAffiliateTraderStatsMock was called with 'true' (the call asserted against in this file) alongside the existing expiry assertion for result.current from useIsRefCodeExpired.
🤖 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/cowswap-frontend/src/modules/affiliate/containers/AffiliateTraderExpiryBanner.tsx`:
- Around line 25-29: The logic treating the exact expiry moment as neither
expired nor pre-expiry causes the banner to vanish briefly; change the expiry
comparison so rewardsEndTimestamp is considered expired when equal to now by
updating isExpired to use <= (i.e. rewardsEndTimestamp <= now) and keep
isPreExpiry using strict > now with the PRE_EXPIRY_WINDOW_MS check so the exact
boundary falls into the expired branch; update the boolean definitions for
rewardsEndTimestamp, isExpired, and isPreExpiry accordingly.
---
Nitpick comments:
In
`@apps/cowswap-frontend/src/modules/affiliate/containers/AffiliateTraderExpiryBanner.test.tsx`:
- Around line 117-131: Add a new unit test in
AffiliateTraderExpiryBanner.test.tsx that mocks useAffiliateTraderStatsMock (via
createTraderStatsResponse) with rewards_end exactly equal to the mocked machine
time from useMachineTimeMsMock (Date.parse(...)), calls renderComponent(), and
asserts the banner/analytics treat this as the expiry boundary: specifically
verify useAffiliateStateViewAnalyticsMock is invoked with the expired payload
(action: 'affiliate_trader_expired_code_viewed') and that the component renders
the expired-banner state accordingly; locate mocks and helpers by names
useAffiliateTraderStatsMock, createTraderStatsResponse, useMachineTimeMsMock,
renderComponent, and useAffiliateStateViewAnalyticsMock.
In
`@apps/cowswap-frontend/src/modules/affiliate/hooks/useIsRefCodeExpired.test.tsx`:
- Around line 91-102: The test title and assertions are misaligned; either make
the title reflect that this test only asserts expiry (e.g., rename the it(...)
string to "still reports expired codes") or add an explicit assertion that
verifies tracking is enabled (ensure useAffiliateTraderStatsMock is called with
the tracking flag true). Locate the test in useIsRefCodeExpired.test.tsx and
update the it(...) description or add/check the assertion that
useAffiliateTraderStatsMock was called with 'true' (the call asserted against in
this file) alongside the existing expiry assertion for result.current from
useIsRefCodeExpired.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 903806f3-2716-46ba-8892-eb44f74b1a4f
📒 Files selected for processing (5)
apps/cowswap-frontend/src/modules/affiliate/analytics/affiliateAnalytics.types.tsapps/cowswap-frontend/src/modules/affiliate/containers/AffiliateTraderExpiryBanner.test.tsxapps/cowswap-frontend/src/modules/affiliate/containers/AffiliateTraderExpiryBanner.tsxapps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeCreate.test.tsxapps/cowswap-frontend/src/modules/affiliate/hooks/useIsRefCodeExpired.test.tsx
✅ Files skipped from review due to trivial changes (1)
- apps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeCreate.test.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/cowswap-frontend/src/modules/affiliate/analytics/affiliateAnalytics.types.ts
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeCreate.ts`:
- Around line 49-53: The telemetry calls to trackAffiliateEvent (the start and
completion events around createCode and in onCreate) must be made best-effort so
they cannot block or cause createCode/onCreate to fail; wrap each
trackAffiliateEvent invocation in a try/catch (or otherwise swallow its errors)
and do not allow thrown errors to propagate to the caller nor to change the
return/throw behavior of createCode or onCreate; ensure any awaited telemetry is
moved inside these try/catch blocks or made non-blocking so the affiliate API
call and its success/failure flow remain independent (apply the same change for
the calls referenced around the start block and the completion/error block in
the createCode/onCreate flow).
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 81ed4901-7f4a-4096-b4ce-3d1770acf9f1
📒 Files selected for processing (6)
apps/cowswap-frontend/src/modules/affiliate/analytics/affiliateAnalytics.test.tsapps/cowswap-frontend/src/modules/affiliate/containers/AffiliateTraderModalCodeLinking.tsxapps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeCreate.test.tsxapps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeCreate.tsapps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliateStateViewAnalytics.tsapps/cowswap-frontend/src/modules/affiliate/state/migrations/affiliateTraderSavedCodeStorage.utils.ts
| trackAffiliateEvent({ | ||
| analytics, | ||
| action: 'affiliate_partner_code_create_started', | ||
| chainId: AFFILIATE_PAYOUTS_CHAIN_ID, | ||
| }) |
There was a problem hiding this comment.
Don't let analytics failures block partner-code creation.
trackAffiliateEvent() is on the main control path here. If analytics throws on the start or completion event, createCode() is skipped or onCreate() rejects, and the user gets a misleading NetworkError even though the affiliate API may be fine. Telemetry should be best-effort around this flow, not part of the transaction boundary.
♻️ Suggested hardening
+function safeTrackAffiliateEvent(params: Parameters<typeof trackAffiliateEvent>[0]): void {
+ try {
+ trackAffiliateEvent(params)
+ } catch {
+ // optionally report via the centralized logger, but never block code creation
+ }
+}
+
export function useAffiliatePartnerCodeCreate({
account,
provider,
code,
setError,
}: UseAffiliatePartnerCodeCreateParams): UseAffiliatePartnerCodeCreateResult {
@@
- trackAffiliateEvent({
+ safeTrackAffiliateEvent({
analytics,
action: 'affiliate_partner_code_create_started',
chainId: AFFILIATE_PAYOUTS_CHAIN_ID,
})
@@
- trackAffiliateEvent({
+ safeTrackAffiliateEvent({
analytics,
action: 'affiliate_partner_code_create_completed',
chainId: AFFILIATE_PAYOUTS_CHAIN_ID,
result: 'success',
})
@@
- trackAffiliateEvent({
+ safeTrackAffiliateEvent({
analytics,
action: 'affiliate_partner_code_create_completed',
chainId: AFFILIATE_PAYOUTS_CHAIN_ID,
result: 'failed',
failureReason: normalizeAffiliatePartnerCodeCreateFailureReason(mappedError),
})Also applies to: 72-89
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@apps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeCreate.ts`
around lines 49 - 53, The telemetry calls to trackAffiliateEvent (the start and
completion events around createCode and in onCreate) must be made best-effort so
they cannot block or cause createCode/onCreate to fail; wrap each
trackAffiliateEvent invocation in a try/catch (or otherwise swallow its errors)
and do not allow thrown errors to propagate to the caller nor to change the
return/throw behavior of createCode or onCreate; ensure any awaited telemetry is
moved inside these try/catch blocks or made non-blocking so the affiliate API
call and its success/failure flow remain independent (apply the same change for
the calls referenced around the start block and the completion/error block in
the createCode/onCreate flow).
|
@elena-zh thanks for reviewing! I checked the exact flow in GTM Preview, and the emitted events look correct on my side. I see affiliate_partner_code_create_started followed by a single affiliate_partner_code_create_completed, plus the expected suggestion/availability/page-view events. I don’t see a second completion event with codeUnavailable. My guess is your screenshot may have been from debug logs, but in the current branch the actual emitted GTM events for this flow look fine (console events is not the same as actual emitted GTM events). |
There was a problem hiding this comment.
♻️ Duplicate comments (1)
apps/cowswap-frontend/src/modules/affiliate/analytics/affiliateAnalytics.utils.ts (1)
71-86:⚠️ Potential issue | 🟠 MajorHandle
TraderWalletStatus.LINKEDexplicitly in page-state mapping.On Line 85, a
LINKEDwallet can still resolve toONBOARDwhenhasSavedCodeis false, which misclassifies trader funnel analytics. Add an explicitLINKEDcase before fallback logic.💡 Suggested fix
export function getAffiliateTraderPageState( walletStatus: TraderWalletStatus, hasSavedCode: boolean, ): AffiliatePageState { + if (walletStatus === TraderWalletStatus.LINKED) { + return AffiliatePageState.LINKED + } + switch (walletStatus) { case TraderWalletStatus.PENDING: return AffiliatePageState.LOADING case TraderWalletStatus.UNSUPPORTED: return AffiliatePageState.UNSUPPORTED case TraderWalletStatus.INELIGIBLE: return AffiliatePageState.INELIGIBLE case TraderWalletStatus.DISCONNECTED: return AffiliatePageState.ONBOARD default: return hasSavedCode ? AffiliatePageState.LINKED : AffiliatePageState.ONBOARD } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/cowswap-frontend/src/modules/affiliate/analytics/affiliateAnalytics.utils.ts` around lines 71 - 86, The mapping in getAffiliateTraderPageState incorrectly lets TraderWalletStatus.LINKED fall through to the fallback that uses hasSavedCode, potentially returning ONBOARD; add an explicit case for TraderWalletStatus.LINKED that returns AffiliatePageState.LINKED before the default/fallback so LINKED wallets are always classified as AffiliatePageState.LINKED regardless of hasSavedCode.
🧹 Nitpick comments (1)
apps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeCreate.test.tsx (1)
128-158: Assert success completion analytics in the “start event throws” path.This test proves creation continues, but it doesn’t verify that
affiliate_partner_code_create_completedwithresult: 'success'is still emitted afterward.♻️ Suggested assertion addition
await act(async () => { await result.current.onCreate() }) expect(provider.getSigner).toHaveBeenCalledTimes(1) expect(createCodeMock).toHaveBeenCalledTimes(1) expect(mutatePartnerInfo).toHaveBeenCalledTimes(1) expect(setError.mock.calls).toEqual([[undefined]]) + + const trackedEvents = sendEvent.mock.calls.map(([event]) => event) + const completedEvents = trackedEvents.filter((event) => event.action === 'affiliate_partner_code_create_completed') + expect(completedEvents).toHaveLength(1) + expect(completedEvents[0]).toMatchObject({ + category: CowSwapAnalyticsCategory.AFFILIATE, + action: 'affiliate_partner_code_create_completed', + chainId: AFFILIATE_PAYOUTS_CHAIN_ID, + result: 'success', + })Based on learnings: "Add additional Jest logic specs or Storybook/Cosmos visual checks as behavior demands."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeCreate.test.tsx` around lines 128 - 158, The test for useAffiliatePartnerCodeCreate that simulates sendEvent throwing should also assert a success completion analytic is still emitted: after calling result.current.onCreate(), add an assertion that sendEvent was called with the 'affiliate_partner_code_create_completed' event and payload containing result: 'success' (and relevant identifiers like code/account if used), while keeping the existing assertions for provider.getSigner, createCodeMock, mutatePartnerInfo and setError; locate useAffiliatePartnerCodeCreate, the test's sendEvent mock, and the 'affiliate_partner_code_create_completed' event name to update the expectations.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In
`@apps/cowswap-frontend/src/modules/affiliate/analytics/affiliateAnalytics.utils.ts`:
- Around line 71-86: The mapping in getAffiliateTraderPageState incorrectly lets
TraderWalletStatus.LINKED fall through to the fallback that uses hasSavedCode,
potentially returning ONBOARD; add an explicit case for
TraderWalletStatus.LINKED that returns AffiliatePageState.LINKED before the
default/fallback so LINKED wallets are always classified as
AffiliatePageState.LINKED regardless of hasSavedCode.
---
Nitpick comments:
In
`@apps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeCreate.test.tsx`:
- Around line 128-158: The test for useAffiliatePartnerCodeCreate that simulates
sendEvent throwing should also assert a success completion analytic is still
emitted: after calling result.current.onCreate(), add an assertion that
sendEvent was called with the 'affiliate_partner_code_create_completed' event
and payload containing result: 'success' (and relevant identifiers like
code/account if used), while keeping the existing assertions for
provider.getSigner, createCodeMock, mutatePartnerInfo and setError; locate
useAffiliatePartnerCodeCreate, the test's sendEvent mock, and the
'affiliate_partner_code_create_completed' event name to update the expectations.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 49e43748-8c5b-4073-8522-e87d431db089
📒 Files selected for processing (3)
apps/cowswap-frontend/src/modules/affiliate/analytics/affiliateAnalytics.test.tsapps/cowswap-frontend/src/modules/affiliate/analytics/affiliateAnalytics.utils.tsapps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeCreate.test.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/cowswap-frontend/src/modules/affiliate/analytics/affiliateAnalytics.test.ts
Hey @fairlighteth , thank you for taking a look at it. |
|
@elena-zh Yes, there is now an event for that: |
Summary
This PR adds affiliate frontend analytics to cowswap-frontend for the core partner and trader funnel.
It instruments:
The new events are:
This PR also includes the supporting logic needed to keep these analytics accurate and non-blocking:
No product UI changes are intended.
To Test
Background
This PR focuses on frontend affiliate funnel analytics only.
A follow-up PR is planned for affiliate attribution on actual order submission flows: