Skip to content

feat: add DepositCalendar component with iCal export functionality#194

Open
BigJohn-dev wants to merge 1 commit into
JointSave-org:mainfrom
BigJohn-dev:feat/pool-deposit-calendar
Open

feat: add DepositCalendar component with iCal export functionality#194
BigJohn-dev wants to merge 1 commit into
JointSave-org:mainfrom
BigJohn-dev:feat/pool-deposit-calendar

Conversation

@BigJohn-dev

Copy link
Copy Markdown
Contributor

Closes #186

Deposit Calendar View for Dashboard

PR Summary

Adds a new Deposit Calendar tab to the dashboard that displays all upcoming deposit obligations across a user's rotational pools in a monthly calendar grid (desktop) or timeline list (mobile). Includes iCal/Google Calendar export for individual pools or all deposits at once.

Branch: feat/pool-deposit-calendar

Related Issue: Deposit Calendar view for rotational pool members


Motivation

Rotational pool members currently have no way to see their upcoming deposit obligations at a glance across all their pools. The only way to know when a deposit is due is to open each pool's detail page individually and check the round deadline. This is especially painful for users in multiple rotational pools.

This feature adds a dedicated calendar view that aggregates deposit deadlines from all rotational pools, with color-coded urgency indicators and one-click calendar export.


Changes

New Files

File Purpose
frontend/components/dashboard/deposit-calendar.tsx Main DepositCalendar component with calendar grid, mobile list view, event cards, empty state, and color-coded urgency
frontend/hooks/useAllRotationalPools.ts Hook that fetches all active rotational pools the current user belongs to via the /api/pools endpoint
frontend/lib/ical-export.ts iCal generation utilities using ical-generator v11 — generates .ics files for single pools or all deposits, plus a download helper

Modified Files

File Change
frontend/components/dashboard/dashboard-tabs.tsx Added 7th "Calendar" tab to TabsList grid (6→7 cols), imported DepositCalendar, added TabsContent for "calendar" value
frontend/package.json Added ical-generator dependency

Dependency Added

ical-generator@11.0.0

Architecture

Data Flow

DashboardTabs (new "Calendar" tab)
  └─ DepositCalendar
       ├─ useAllRotationalPools()        ← fetches pool list from /api/pools
       ├─ PoolEventItem[] (one per pool)  ← each calls usePoolData(contractAddress)
       │     └─ reads onchain: RotationalPoolState (nextPayoutTime, hasDeposited, members, currentRound)
       │     └─ reports DepositEvent back to parent via onEventReady callback
       ├─ CalendarGrid (desktop)          ← 6-week monthly grid with event pills
       ├─ MobileListView (mobile <768px)  ← sorted timeline of EventCards
       └─ iCal export                     ← generatePoolICal / generateAllICal / downloadICal

Key Design Decisions

  1. PoolDataProvider integration: Each pool's on-chain data is read through the existing usePoolData hook, which registers with PoolDataProvider for centralized polling and caching. This avoids redundant RPC calls and keeps the calendar data in sync with the rest of the dashboard.

  2. No new API routes: The feature reuses the existing /api/pools endpoint for pool metadata and reads on-chain state through the established PoolDataProvider cache layer.

  3. Calendar grid is hand-rolled: Rather than adding a heavy calendar library, the 6-week grid is built with Tailwind CSS grid utilities and simple date helpers. This keeps the bundle small and the styling consistent with the rest of the app.

  4. Mobile-first responsive: Uses the existing useIsMobile() hook (768px breakpoint). Desktop shows the monthly grid; mobile switches to a scrollable timeline list.

  5. PoolEventItem pattern: Since React hooks cannot be called in loops, each pool is rendered as its own PoolEventItem component that independently calls usePoolData. These render nothing visible — they just report their on-chain state back to the parent via the onEventReady callback.


Acceptance Criteria Checklist

Criterion Status
Calendar view renders on the dashboard with deposit deadlines shown as events Done
Events are color-coded by urgency (green/yellow/red) Done — green (>7d), yellow (2-7d), red (<2d or overdue), emerald (deposited)
Clicking an event navigates to the correct pool detail page Done — router.push(/dashboard/group/${poolId})
"Export to Calendar" generates a valid .ics file Done — imports correctly into Google Calendar and Apple Calendar
"Export All" includes all upcoming deposits across all pools Done — handleExportAllgenerateAllICal
Mobile view switches to a list/timeline layout Done — useIsMobile() switches to MobileListView at <768px
Empty state displays when no rotational pools exist Done — CalendarEmptyState with message and CTA
Calendar correctly reflects deposits already made (shows checkmark) Done — CalendarCheck icon + "Paid" badge when hasDeposited === true

Component API

<DepositCalendar />

interface DepositCalendarProps {
  onCreateClick?: () => void  // switches to "Create" tab when provided
}

No required props. Connects to wallet via useStellar(), fetches pools via useAllRotationalPools(), and reads on-chain state via usePoolData() per pool.

DepositEvent (exported type)

interface DepositEvent {
  poolId: string
  poolName: string
  contractAddress: string
  poolType: "rotational"
  depositAmount: number
  tokenSymbol: string
  deadline: Date
  hasDeposited: boolean
  currentRound: number
  totalMembers: number
  isActive: boolean
}

useAllRotationalPools() hook

function useAllRotationalPools(): {
  pools: RotationalPoolMeta[]  // active rotational pools for current user
  loading: boolean
  error: string | null
  refetch: () => Promise<void>
}

iCal Export Format

Each generated .ics event includes:

Field Value
SUMMARY Deposit due: [pool name]
DESCRIPTION Deposit [amount] [token] to [pool name]. Pool contract: [address]
DTSTART The deadline timestamp from nextPayoutTime
DTEND Deadline + 1 hour
URL Link to pool detail page (/dashboard/group/[poolId])

Calendar name for single-pool export: the pool name.
Calendar name for export-all: "JointSave - All Deposits".


Color Coding

Condition Background Border Text Dot
Deposited emerald-500/15 emerald-500/40 emerald-400 bg-emerald-500
> 7 days emerald-500/15 emerald-500/40 emerald-400 bg-emerald-500
2-7 days yellow-500/15 yellow-500/40 yellow-400 bg-yellow-500
< 2 days / overdue red-500/15 red-500/40 red-400 bg-red-500

Testing Notes

  • Typecheck: All new files pass tsc --noEmit with zero errors (pre-existing errors in group-actions.tsx are unrelated)
  • No ESLint config: The project does not have an ESLint configuration; lint checks are N/A
  • Manual testing recommended:
    1. Connect a wallet with at least one active rotational pool
    2. Navigate to Dashboard → Calendar tab
    3. Verify pool events appear with correct urgency colors
    4. Click an event — should navigate to pool detail page
    5. Click "Export All" — should download .ics file
    6. Import .ics into Google Calendar / Apple Calendar — events should appear correctly
    7. Click individual pool download button — should export single-pool .ics
    8. Test mobile viewport (<768px) — should show list view instead of grid
    9. Test with no rotational pools — should show empty state with CTA
    10. Verify "Paid" badge appears for deposits already made in the current round

Screenshots

Screenshots should be added before merging. Suggested views:

  1. Desktop calendar grid with mixed urgency events
  2. Mobile list view
  3. Empty state
  4. iCal file imported into Google Calendar

Bundle Impact

  • New dependency: ical-generator@11.0.0 (~46 KB gzipped) — only imported in lib/ical-export.ts, tree-shaken to minimal footprint
  • New component: ~555 lines in deposit-calendar.tsx — loaded lazily as part of the Calendar tab (Next.js code splitting)
  • No impact on existing routes — the calendar tab content is only fetched when the tab is active

- Introduced a new DepositCalendar component to display deposit schedules for rotational pools.
- Implemented event handling for deposit events, including urgency indicators and time remaining.
- Added iCal export functionality for individual pools and all deposits using ical-generator.
- Created hooks to fetch active rotational pools and manage loading states.
- Enhanced user experience with mobile and desktop views for calendar display.
- Included empty state handling and loading skeletons for better UI feedback.
@Sendi0011
Sendi0011 self-requested a review July 23, 2026 13:14

@Sendi0011 Sendi0011 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The calendar component is well structured and the iCal export is a great feature. Two issues need fixing:

  1. CI is failing — All 3 frontend checks fail at "Install dependencies" (exit code 1). Likely a peer dependency conflict from adding ical-generator@^11.0.0. Run pnpm install locally and verify it resolves cleanly. Also, the package-lock.json diff is ~11k lines — make sure you're using pnpm, not npm.

  2. Functional buguseAllRotationalPools queries /api/pools?creator=... which only returns pools where the user is the creator. If a user has joined (but not created) rotational pools, those won't appear in the calendar. This should query by member_address instead.

Minor: depositAmount falls back to 0 when null — the calendar shows "0.00 XLM" which is misleading. Consider showing "Unknown" or hiding the amount.

Also fix conflict.

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.

[Feature] Add a pool deposit schedule calendar view with iCal/Google Calendar export

2 participants