[TASK-7762] fix: fee calculation after promo code is applied#585
[TASK-7762] fix: fee calculation after promo code is applied#585
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
WalkthroughThis pull request introduces changes to manage promotional codes across various components in the cashout and offramp processes. Modifications include the addition of new properties to interfaces, state management for promo codes, and updates to components to support external handling of promo codes. The changes aim to create a more cohesive and centralized approach to promo code management throughout the user transaction experience. Changes
Possibly related PRs
Suggested reviewers
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
src/components/Offramp/Offramp.consts.ts (1)
Line range hint
82-117: Consider improving promo code managementThe current implementation of
VALID_PROMO_CODEShas several limitations:
- Hardcoded values make it difficult to manage expiration dates
- No validation rules or restrictions
- Updates require code changes and deployment
Consider moving this to a configuration service or database for easier management and to support features like:
- Expiration dates
- Usage limits
- Discount amounts
- Dynamic validation rules
src/components/Offramp/PromoCodeChecker.tsx (2)
Line range hint
31-67: Consider UX improvements for promo code validationThe current implementation could benefit from:
- Debouncing input changes to prevent rapid state updates
- Adding loading state for future async validation
- Case-insensitive comparison during typing
Example implementation:
+ import { debounce } from 'lodash' + + const [isValidating, setIsValidating] = useState(false) + + const debouncedValidation = debounce((code: string) => { + setIsValidating(true) + // Future async validation logic here + const normalizedCode = code.trim().toUpperCase() + // ... rest of validation logic + setIsValidating(false) + }, 300) + const handlePromoCodeSubmit = () => { + setIsValidating(true) const normalizedCode = promoCheckerState.code.trim().toUpperCase() // ... rest of the function + setIsValidating(false) }
Line range hint
79-134: Consider accessibility improvementsThe current implementation could benefit from:
- Adding ARIA labels for screen readers
- Keyboard navigation support
- Focus management after successful application
Example improvements:
- <div className="w-full"> + <div className="w-full" role="region" aria-label="Promo code section"> <div onClick={handleExpandToggle} + onKeyPress={(e) => e.key === 'Enter' && handleExpandToggle()} + role="button" + tabIndex={0} className="flex w-full cursor-pointer flex-row items-center" >
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
src/components/Cashout/Cashout.consts.ts(1 hunks)src/components/Cashout/Cashout.tsx(2 hunks)src/components/Offramp/Confirm.view.tsx(3 hunks)src/components/Offramp/Offramp.consts.ts(1 hunks)src/components/Offramp/PromoCodeChecker.tsx(3 hunks)src/components/Offramp/Success.view.tsx(2 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
src/components/Offramp/Success.view.tsx
[error] 38-38: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.
(lint/suspicious/noGlobalIsNan)
[error] 44-44: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.
(lint/suspicious/noGlobalIsNan)
[error] 44-44: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.
(lint/suspicious/noGlobalIsNan)
🔇 Additional comments (10)
src/components/Cashout/Cashout.consts.ts (1)
70-70: LGTM! Consistent type definition for promo code handling
The callback signature matches the pattern used across components and properly handles both application and removal of promo codes.
src/components/Offramp/Offramp.consts.ts (1)
64-65: LGTM! Consistent interface updates
The promo code related props are consistently defined across interfaces.
src/components/Offramp/PromoCodeChecker.tsx (3)
19-22: LGTM! Well-defined props interface
Clean and type-safe props definition with proper nullability handling.
24-29: LGTM! Clean state initialization
Good practice initializing state with the external promo code while maintaining local state structure.
68-77: LGTM! Proper external state sync
The useEffect hook correctly synchronizes the component's state with external changes.
src/components/Cashout/Cashout.tsx (1)
12-12: LGTM: Clean implementation of promo code state management.
The changes follow React best practices by:
- Managing promo code state at the parent level
- Passing state and callback props to child components
- Following unidirectional data flow pattern
Also applies to: 179-180
src/components/Offramp/Success.view.tsx (1)
66-66: LGTM: Fee display logic correctly handles promo codes.
The fee display logic properly:
- Shows $0 when promo code is applied
- Maintains existing fee structure when no promo code
- Provides clear user feedback through MoreInfo component
Also applies to: 70-74
src/components/Offramp/Confirm.view.tsx (3)
98-100: LGTM: Clean implementation of promo code handling.
The handlePromoCodeApplied function correctly delegates to the parent's callback.
860-863: LGTM: PromoCodeChecker component integration.
The PromoCodeChecker component is properly integrated with:
- Callback for handling promo code application
- Current promo code state
99-99: Verify promo code handling in parent components.
Let's ensure the promo code state management is properly implemented in parent components.
✅ Verification successful
Promo code handling is properly implemented
The promo code implementation is well-structured with proper state management:
- PromoCodeChecker component handles validation against VALID_PROMO_CODES
- Parent components receive updates via onPromoCodeApplied callback
- Two-way binding is maintained with appliedPromoCode prop
- Error handling and UI states are properly managed
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for promo code state management in parent components
# Search for promo code state management
rg -A 5 'const \[appliedPromoCode'
# Search for promo code prop types
ast-grep --pattern 'interface $_ {
$$$
appliedPromoCode: $_
$$$
}'
Length of output: 967
Script:
#!/bin/bash
# Let's verify how the promo code is being handled in the Cashout component
ast-grep --pattern 'function Cashout($_) {
$$$
setAppliedPromoCode($_)
$$$
}'
# Check for promo code usage in the component
rg -A 5 'setAppliedPromoCode\(' src/components/Cashout/Cashout.tsx
# Check how PromoCodeChecker is used
rg -A 5 '<PromoCodeChecker'
Length of output: 680
Script:
#!/bin/bash
# Let's check the implementation of Confirm.view.tsx to understand the promo code handling
ast-grep --pattern 'function $_ {
$$$
handlePromoCodeApplied($_) {
$$$
}
$$$
}'
# Check for promo code state in Confirm.view.tsx
rg -B 5 'handlePromoCodeApplied' src/components/Offramp/Confirm.view.tsx
# Check the PromoCodeChecker component implementation
cat src/components/Offramp/PromoCodeChecker.tsx
Length of output: 5661
| const calculateAmount = () => { | ||
| if (offrampType === _consts.OfframpType.CASHOUT) { | ||
| // add safeguards for usd value | ||
| if (!usdValue || isNaN(parseFloat(usdValue))) { | ||
| return '0.00' | ||
| } | ||
| return utils.formatTokenAmount(parseFloat(usdValue)) | ||
| } else { | ||
| // for CLAIM type | ||
| if (!tokenPrice || !claimLinkData || isNaN(tokenPrice) || isNaN(parseFloat(claimLinkData.tokenAmount))) { | ||
| return '0.00' | ||
| } | ||
| return utils.formatTokenAmount(tokenPrice * parseFloat(claimLinkData.tokenAmount)) | ||
| } | ||
| } |
There was a problem hiding this comment.
Replace global isNaN with Number.isNaN for safer type checking.
The calculateAmount function implementation is good, but uses the global isNaN which can lead to unexpected type coercion.
Apply this diff to fix the type checking:
const calculateAmount = () => {
if (offrampType === _consts.OfframpType.CASHOUT) {
// add safeguards for usd value
- if (!usdValue || isNaN(parseFloat(usdValue))) {
+ if (!usdValue || Number.isNaN(parseFloat(usdValue))) {
return '0.00'
}
return utils.formatTokenAmount(parseFloat(usdValue))
} else {
// for CLAIM type
- if (!tokenPrice || !claimLinkData || isNaN(tokenPrice) || isNaN(parseFloat(claimLinkData.tokenAmount))) {
+ if (!tokenPrice || !claimLinkData || Number.isNaN(tokenPrice) || Number.isNaN(parseFloat(claimLinkData.tokenAmount))) {
return '0.00'
}
return utils.formatTokenAmount(tokenPrice * parseFloat(claimLinkData.tokenAmount))
}
}📝 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.
| const calculateAmount = () => { | |
| if (offrampType === _consts.OfframpType.CASHOUT) { | |
| // add safeguards for usd value | |
| if (!usdValue || isNaN(parseFloat(usdValue))) { | |
| return '0.00' | |
| } | |
| return utils.formatTokenAmount(parseFloat(usdValue)) | |
| } else { | |
| // for CLAIM type | |
| if (!tokenPrice || !claimLinkData || isNaN(tokenPrice) || isNaN(parseFloat(claimLinkData.tokenAmount))) { | |
| return '0.00' | |
| } | |
| return utils.formatTokenAmount(tokenPrice * parseFloat(claimLinkData.tokenAmount)) | |
| } | |
| } | |
| const calculateAmount = () => { | |
| if (offrampType === _consts.OfframpType.CASHOUT) { | |
| // add safeguards for usd value | |
| if (!usdValue || Number.isNaN(parseFloat(usdValue))) { | |
| return '0.00' | |
| } | |
| return utils.formatTokenAmount(parseFloat(usdValue)) | |
| } else { | |
| // for CLAIM type | |
| if (!tokenPrice || !claimLinkData || Number.isNaN(tokenPrice) || Number.isNaN(parseFloat(claimLinkData.tokenAmount))) { | |
| return '0.00' | |
| } | |
| return utils.formatTokenAmount(tokenPrice * parseFloat(claimLinkData.tokenAmount)) | |
| } | |
| } |
🧰 Tools
🪛 Biome (1.9.4)
[error] 38-38: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.
(lint/suspicious/noGlobalIsNan)
[error] 44-44: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.
(lint/suspicious/noGlobalIsNan)
[error] 44-44: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.
(lint/suspicious/noGlobalIsNan)
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
src/components/Offramp/Success.view.tsx (1)
Line range hint
59-67: Extract repeated promo code messages into a reusable function.The promo code and fee explanation messages are duplicated in multiple places. Consider extracting these into a reusable function for better maintainability.
+ const getFeeMessage = (appliedPromoCode: string | undefined, accountType: string) => { + if (appliedPromoCode) { + return `Fees waived with promo code ${appliedPromoCode}`; + } + return `For ${accountType === 'iban' ? 'SEPA' : 'ACH'} transactions a fee of ${ + accountType === 'iban' ? '$1' : '$0.50' + } is charged.`; + }; <MoreInfo - text={ - appliedPromoCode - ? `Fees waived with promo code ${appliedPromoCode}` - : `For ${accountType === 'iban' ? 'SEPA' : 'ACH'} transactions a fee of ${ - accountType === 'iban' ? '$1' : '$0.50' - } is charged.` - } + text={getFeeMessage(appliedPromoCode, accountType)} />
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/components/Offramp/Success.view.tsx(3 hunks)
🔇 Additional comments (1)
src/components/Offramp/Success.view.tsx (1)
Line range hint 1-150: Verify financial calculations and edge cases.
Since this component handles financial calculations and fee processing, please ensure:
- All monetary values are handled with proper precision
- Edge cases like zero amounts, missing values, and invalid inputs are handled correctly
- Fee calculations are consistent with business requirements
✅ Verification successful
Based on the gathered information, I can now generate the final response:
Financial calculations and edge cases have been verified.
The implementation shows proper handling of financial calculations with several safety measures in place:
- Fees are correctly calculated based on account type (SEPA: $1.00, ACH: $0.50) with promo code support
- Token amounts are properly formatted using
utils.formatTokenAmountwhich handles decimal precision - Edge cases are handled:
- Promo codes zeroing out fees
- Null/undefined checks for
usdValueandtokenAmount - Proper decimal handling through
parseFloatand formatting utilities
- Fee calculations are consistent across both the success and confirmation views
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for other financial calculation implementations to verify consistency
rg -A 5 "formatTokenAmount|parseFloat|calculateFee" --type typescript
# Look for test files covering these calculations
fd -e test.tsx -e spec.tsx -e test.ts -e spec.ts | xargs rg "OfframpSuccessView|calculateFee"
Length of output: 202
Script:
#!/bin/bash
# Search for financial calculations with proper file extensions
rg -A 5 "formatTokenAmount|parseFloat|calculateFee" --type ts --type tsx
# Look for test files and their contents
fd -e .test.tsx -e .spec.tsx -e .test.ts -e .spec.ts | xargs cat
# Search for utils.formatTokenAmount usage
rg "utils\.formatTokenAmount" --type ts --type tsx -A 2
# Search for similar fee calculation patterns
rg "fee.*iban|SEPA.*fee|ACH.*fee" --type ts --type tsx -A 2
Length of output: 10398
Script:
#!/bin/bash
# Try searching without file type specifications
rg "utils\.formatTokenAmount" -A 2
# Search for the utils file that contains formatTokenAmount
fd formatTokenAmount
# Look for fee-related calculations
rg "calculateFee|fee.*iban|SEPA.*fee|ACH.*fee" -A 3
# Search for the utils file
fd "utils.(ts|js)" --type f
Length of output: 11981
Script:
#!/bin/bash
# Check the implementation of formatTokenAmount in utils
cat src/utils/format.utils.ts
# Look for any other fee-related calculations in utils files
cat src/utils/cashout.utils.ts
# Check for any validation or error handling related to amounts
rg "parseFloat|Number|\.toFixed" src/utils/
Length of output: 25893
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Refactor