Skip to content

Added insights screen functionality#7

Merged
saikumarbt merged 1 commit into
mainfrom
insights_screen
Jul 10, 2026
Merged

Added insights screen functionality#7
saikumarbt merged 1 commit into
mainfrom
insights_screen

Conversation

@saikumarbt

Copy link
Copy Markdown
Owner

No description provided.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

Summary by CodeRabbit

  • New Features
    • Added a new Monthly Insights view with a chart, expense summary, and expandable subscription history.
    • The Upcoming list now updates from live subscription data and is sorted by the nearest renewal date.
  • Bug Fixes
    • Removed cancelled subscriptions from the Upcoming section.
    • Improved renewal timing so past dates roll forward correctly and display more accurate days remaining.

Walkthrough

Adds 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.

Changes

Insights feature and renewals computation

Layer / File(s) Summary
Renewal date utility and shared types
lib/utils.ts, type.d.ts
Adds getDaysUntilRenewal to compute days until renewal (rolling forward yearly/monthly if past), and adds global interfaces WeeklyInsight, InsightsSummary, InsightsModalProps.
Upcoming renewals in home tab
app/(tabs)/index.tsx
Computes upcomingRenewals via useMemo from live subscriptions, filtering cancelled items and sorting by daysLeft from getDaysUntilRenewal; wires the "Upcoming" FlatList to this memo.
Insights data and styling
constants/data.ts, global.css
Adds INSIGHTS_WEEKLY and INSIGHTS_SUMMARY constants and .insights-* CSS classes for header, chart/bars/tooltips, and expense cards.
Insights component implementation
components/Insights.tsx
New default-export component renders header, weekly bar chart with tooltips, expenses summary card, and a history list of SubscriptionCard items with expand/collapse state, using SubscriptionsContext.
insights.tsx screen re-export
app/(tabs)/insights.tsx
Replaces the previous inline screen implementation with a direct re-export of the default export from components/Insights.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

Poem

A rabbit charts each coin that's spent,
Bars rise up like grass that's bent,
Days-till-renewal, hop and count,
Tooltips pop with small amount,
New screens bloom where old ones went — 🐰📊✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive No pull request description was provided, so the check cannot assess meaningful relevance. Add a brief PR description summarizing the main changes, or provide the intended description for review.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change: adding an insights screen and related functionality.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch insights_screen

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.css

File 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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 9559d95 and d128a4d.

📒 Files selected for processing (7)
  • app/(tabs)/index.tsx
  • app/(tabs)/insights.tsx
  • components/Insights.tsx
  • constants/data.ts
  • global.css
  • lib/utils.ts
  • type.d.ts

Comment thread app/(tabs)/index.tsx
Comment on lines +39 to +45
daysLeft:
getDaysUntilRenewal(
subscription.renewalDate,
subscription.billing,
) ?? Infinity,
}))
.sort((a, b) => a.daysLeft - b.daysLeft),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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 -l

Repository: 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.

Comment thread components/Insights.tsx
import { Image, ScrollView, Text, View } from "react-native";
import { SafeAreaView as RNSafeAreaView } from "react-native-safe-area-context";

const SafeAreaView = styled(RNSafeAreaView) as any;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 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.

Comment thread components/Insights.tsx
Comment on lines +16 to +22
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);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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.

Suggested change
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.

Comment thread lib/utils.ts
Comment on lines +66 to +68
const today = dayjs().startOf("day");
let next = dayjs(renewalDate).startOf("day");
if (!next.isValid()) return null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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 -n

Repository: 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 -n

Repository: 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.

@saikumarbt
saikumarbt merged commit eab6d49 into main Jul 10, 2026
1 check passed
@saikumarbt
saikumarbt deleted the insights_screen branch July 10, 2026 06:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant