Added insights screen functionality#7
Conversation
Summary by CodeRabbit
WalkthroughAdds a new Insights component rendering a Monthly Insights screen (chart, expenses summary, history) backed by new constants, types, and CSS classes, replaces app/(tabs)/insights.tsx with a re-export of it, and refactors the home tab's "Upcoming" list to compute renewals from live subscriptions using a new getDaysUntilRenewal utility. ChangesInsights feature and renewals computation
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 Biome (2.5.1)global.cssFile contains syntax errors that prevent linting: Line 7: Tailwind-specific syntax is disabled.; Line 50: Tailwind-specific syntax is disabled.; Line 54: Tailwind-specific syntax is disabled.; Line 58: Tailwind-specific syntax is disabled.; Line 62: Tailwind-specific syntax is disabled.; Line 66: Tailwind-specific syntax is disabled.; Line 70: Tailwind-specific syntax is disabled.; Line 74: Tailwind-specific syntax is disabled.; Line 78: Tailwind-specific syntax is disabled.; Line 82: Tailwind-specific syntax is disabled.; Line 86: Tailwind-specific syntax is disabled.; Line 90: Tailwind-specific syntax is disabled.; Line 94: Tailwind-specific syntax is disabled.; Line 98: Tailwind-specific syntax is disabled.; Line 102: Tailwind-specific syntax is disabled.; Line 106: Tailwind-specific syntax is disabled.; Line 110: Tailwind-specific syntax is disabled.; Line 114: Tailwind-specific syntax is disabled.; Line 118: Tailwind-specific syntax is disabled.; Line 122: Tailwind-specific syntax ... [truncated 3721 characters] ... tax is disabled.; Line 430: Tailwind-specific syntax is disabled.; Line 434: Tailwind-specific syntax is disabled.; Line 438: Tailwind-specific syntax is disabled.; Line 442: Tailwind-specific syntax is disabled.; Line 446: Tailwind-specific syntax is disabled.; Line 450: Tailwind-specific syntax is disabled.; Line 454: Tailwind-specific syntax is disabled.; Line 458: Tailwind-specific syntax is disabled.; Line 462: Tailwind-specific syntax is disabled.; Line 466: Tailwind-specific syntax is disabled.; Line 470: Tailwind-specific syntax is disabled.; Line 474: Tailwind-specific syntax is disabled.; Line 478: Tailwind-specific syntax is disabled.; Line 482: Tailwind-specific syntax is disabled.; Line 486: Tailwind-specific syntax is disabled.; Line 490: Tailwind-specific syntax is disabled. 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: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/`(tabs)/index.tsx:
- Around line 39-45: The upcoming subscription list is assigning `Infinity` to
`daysLeft` when `getDaysUntilRenewal` cannot compute a renewal date, which then
renders as a bad label in `UpcomingSubscriptionCard`. Update the mapping in
`index.tsx` so entries with an uncomputable renewal date are either filtered out
before sorting/rendering or converted to an explicit fallback value like
"Unknown" instead of `Infinity`. Use the existing `getDaysUntilRenewal` mapping
and the `UpcomingSubscriptionCard` consumer to keep the fix localized.
In `@components/Insights.tsx`:
- Line 14: The styled SafeAreaView declaration is using an `as any` cast that
hides type errors in `SafeAreaView`/`RNSafeAreaView`. Update the `SafeAreaView`
setup in `Insights.tsx` to avoid the unsafe cast, either by introducing a
properly typed wrapper for the NativeWind styled component or by replacing it
with an explicit lint suppression plus a brief comment that documents the
NativeWind limitation.
- Around line 16-22: The chart scale in Insights.tsx is inconsistent because
AXIS_LABELS are laid out as a 5–45 axis while barHeightFor() computes heights
from a 0-based baseline. Update the axis labels/gridline logic to use the same
0-to-AXIS_MAX scale as barHeightFor, and adjust the label set and positioning in
the chart rendering so the bars and gridlines align consistently.
In `@lib/utils.ts`:
- Around line 66-68: Treat missing renewal dates as invalid in
getDaysUntilRenewal by ensuring dayjs does not receive undefined; update the
renewalDate handling in lib/utils.ts so undefined is coerced to an invalid input
or returned as null before parsing. Keep the existing today/next logic in place,
but make sure the function returns null for undefined renewalDate so the
Upcoming list fallback behavior is preserved.
🪄 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: ASSERTIVE
Plan: Pro
Run ID: 22a4e1d2-6dce-4d2d-b839-cadcc01474c1
📒 Files selected for processing (7)
app/(tabs)/index.tsxapp/(tabs)/insights.tsxcomponents/Insights.tsxconstants/data.tsglobal.csslib/utils.tstype.d.ts
| daysLeft: | ||
| getDaysUntilRenewal( | ||
| subscription.renewalDate, | ||
| subscription.billing, | ||
| ) ?? Infinity, | ||
| })) | ||
| .sort((a, b) => a.daysLeft - b.daysLeft), |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Find UpcomingSubscriptionCard implementation to check daysLeft rendering.
rg -nP 'daysLeft|days left|daysLeft' --type=tsx -C3 $(fd -t f 'UpcomingSubscriptionCard' --type f)
# If the file isn't found by name, search for the component definition
rg -nP 'UpcomingSubscriptionCard|UpcomingSubscription' --type=tsx -lRepository: saikumarbt/react-native-recurly
Length of output: 225
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the relevant files and inspect the component rendering daysLeft.
fd -a 'UpcomingSubscriptionCard' .
fd -a 'index.tsx' app
fd -a 'type.d.ts' .
# Search for the component and any daysLeft rendering.
rg -n -C 3 'UpcomingSubscriptionCard|daysLeft|days left|Unknown|Infinity' app components .Repository: saikumarbt/react-native-recurly
Length of output: 8064
Handle uncomputable renewal dates before assigning daysLeft
UpcomingSubscriptionCard renders daysLeft directly, so Infinity becomes "Infinity days left" here. Filter these items out or map them to an explicit fallback like "Unknown" before passing them to the card.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/`(tabs)/index.tsx around lines 39 - 45, The upcoming subscription list is
assigning `Infinity` to `daysLeft` when `getDaysUntilRenewal` cannot compute a
renewal date, which then renders as a bad label in `UpcomingSubscriptionCard`.
Update the mapping in `index.tsx` so entries with an uncomputable renewal date
are either filtered out before sorting/rendering or converted to an explicit
fallback value like "Unknown" instead of `Infinity`. Use the existing
`getDaysUntilRenewal` mapping and the `UpcomingSubscriptionCard` consumer to
keep the fix localized.
| import { Image, ScrollView, Text, View } from "react-native"; | ||
| import { SafeAreaView as RNSafeAreaView } from "react-native-safe-area-context"; | ||
|
|
||
| const SafeAreaView = styled(RNSafeAreaView) as any; |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
Consider removing the as any cast on SafeAreaView.
The as any cast suppresses type safety for the styled SafeAreaView. While this is a known NativeWind limitation, a typed wrapper or a // eslint-disable-next-line with a brief comment would make the intent explicit and prevent the cast from masking future type errors.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@components/Insights.tsx` at line 14, The styled SafeAreaView declaration is
using an `as any` cast that hides type errors in
`SafeAreaView`/`RNSafeAreaView`. Update the `SafeAreaView` setup in
`Insights.tsx` to avoid the unsafe cast, either by introducing a properly typed
wrapper for the NativeWind styled component or by replacing it with an explicit
lint suppression plus a brief comment that documents the NativeWind limitation.
| const CHART_HEIGHT = 200; | ||
| const AXIS_MAX = 45; | ||
| const AXIS_LABELS = [45, 35, 25, 15, 5]; | ||
| const PRIMARY = "#081126"; | ||
|
|
||
| const barHeightFor = (amount: number) => | ||
| Math.max((amount / AXIS_MAX) * CHART_HEIGHT, 6); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Chart axis scale mismatch causes bars to misalign with gridlines.
AXIS_LABELS = [45, 35, 25, 15, 5] are placed with justify-between in a 200px container, assuming an evenly-spaced 5-to-45 scale. However, barHeightFor uses amount / AXIS_MAX which assumes a 0-to-45 baseline. This creates a visible discrepancy: for value 20, the bar top lands at ~111px from the top, but the corresponding position on the 5–45 axis scale would be 125px — a 14px gap.
📊 Proposed fix: use a consistent 0-based scale
-const AXIS_MAX = 45;
-const AXIS_LABELS = [45, 35, 25, 15, 5];
+const AXIS_MAX = 40;
+const AXIS_LABELS = [40, 30, 20, 10, 0];This keeps nice round numbers, ensures the bottom label (0) aligns with the bar baseline, and gridlines match the linear scale used by barHeightFor.
📝 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 CHART_HEIGHT = 200; | |
| const AXIS_MAX = 45; | |
| const AXIS_LABELS = [45, 35, 25, 15, 5]; | |
| const PRIMARY = "#081126"; | |
| const barHeightFor = (amount: number) => | |
| Math.max((amount / AXIS_MAX) * CHART_HEIGHT, 6); | |
| const CHART_HEIGHT = 200; | |
| const AXIS_MAX = 40; | |
| const AXIS_LABELS = [40, 30, 20, 10, 0]; | |
| const PRIMARY = "`#081126`"; | |
| const barHeightFor = (amount: number) => | |
| Math.max((amount / AXIS_MAX) * CHART_HEIGHT, 6); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@components/Insights.tsx` around lines 16 - 22, The chart scale in
Insights.tsx is inconsistent because AXIS_LABELS are laid out as a 5–45 axis
while barHeightFor() computes heights from a 0-based baseline. Update the axis
labels/gridline logic to use the same 0-to-AXIS_MAX scale as barHeightFor, and
adjust the label set and positioning in the chart rendering so the bars and
gridlines align consistently.
| const today = dayjs().startOf("day"); | ||
| let next = dayjs(renewalDate).startOf("day"); | ||
| if (!next.isValid()) return null; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Verify that dayjs(undefined) returns current date, not invalid date.
# This confirms the bug where undefined renewalDate produces 0 instead of null.
node -e "
const dayjs = require('dayjs');
console.log('dayjs(undefined).isValid():', dayjs(undefined).isValid());
console.log('dayjs(undefined) equals now:', dayjs(undefined).format() === dayjs().format());
console.log('dayjs(\"\").isValid():', dayjs('').isValid());
console.log('dayjs(null).isValid():', dayjs(null).isValid());
"Repository: saikumarbt/react-native-recurly
Length of output: 292
🏁 Script executed:
#!/bin/bash
set -e
printf '%s\n' '--- lib/utils.ts ---'
nl -ba lib/utils.ts | sed -n '55,95p'
printf '%s\n' '--- index.tsx ---'
nl -ba index.tsx | sed -n '1,220p'Repository: saikumarbt/react-native-recurly
Length of output: 280
🏁 Script executed:
#!/bin/bash
set -e
echo '--- lib/utils.ts ---'
sed -n '1,140p' lib/utils.ts | cat -n
echo '--- index.tsx ---'
sed -n '1,240p' index.tsx | cat -nRepository: saikumarbt/react-native-recurly
Length of output: 3196
🏁 Script executed:
#!/bin/bash
set -e
echo '--- getDaysUntilRenewal usages ---'
rg -n "getDaysUntilRenewal|daysLeft|renewalDate" . --glob '!node_modules' --glob '!android' --glob '!ios'Repository: saikumarbt/react-native-recurly
Length of output: 1853
🏁 Script executed:
#!/bin/bash
set -e
echo '--- app/(tabs)/index.tsx ---'
sed -n '1,220p' 'app/(tabs)/index.tsx' | cat -n
echo '--- components/UpcomingSubscriptionCard.tsx ---'
sed -n '1,120p' components/UpcomingSubscriptionCard.tsx | cat -n
echo '--- type.d.ts ---'
sed -n '1,120p' type.d.ts | cat -nRepository: saikumarbt/react-native-recurly
Length of output: 9633
Treat undefined renewal dates as invalid
dayjs(undefined) resolves to today, so getDaysUntilRenewal(undefined, ...) returns 0 and bypasses the ?? Infinity fallback. Missing renewal dates then bubble to the top of the Upcoming list and render as “Last Day”. Coerce renewalDate to "" (or return null early) so undefined stays invalid.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@lib/utils.ts` around lines 66 - 68, Treat missing renewal dates as invalid in
getDaysUntilRenewal by ensuring dayjs does not receive undefined; update the
renewalDate handling in lib/utils.ts so undefined is coerced to an invalid input
or returned as null before parsing. Keep the existing today/next logic in place,
but make sure the function returns null for undefined renewalDate so the
Upcoming list fallback behavior is preserved.
No description provided.