Skip to content

Add Interactive Leaflet Map Visualizing Global Fundable Streams - #576

Open
RemmyAcee wants to merge 2 commits into
Fundable-Protocol:mainfrom
RemmyAcee:fundable
Open

Add Interactive Leaflet Map Visualizing Global Fundable Streams#576
RemmyAcee wants to merge 2 commits into
Fundable-Protocol:mainfrom
RemmyAcee:fundable

Conversation

@RemmyAcee

@RemmyAcee RemmyAcee commented Jul 29, 2026

Copy link
Copy Markdown

Close: #522

All done. Here's a summary of the changes made:
Files Modified (6)

  • types.ts — Added FundableMapFilters interface, isLoading, filters, onStreamSelect, onFilterChange props
  • cluster-utils.ts — Added filterStreams() (status/category/search filtering) and getCategories() (unique sorted categories)
  • FundableMapView.tsx — Interactive hover states on markers (brightens, enlarges on hover), keyboard-accessible popup items with focus/hover styles, loading overlay, empty state with icon, responsive min-h-[300px] sm:min-h-[400px] sizing, aria-label + role="button" on markers
  • FundableMap.tsx — Error boundary wrapping (reuses ErrorBoundary from the codebase), StatusLegend overlay showing active/funded/pending counts by color, filter passthrough, custom error fallback UI with retry
  • FundableMap.test.tsx — From 10 to 54 tests covering: filter logic (12 tests), getCategories, getStatusColor, getClusterColor/getClusterRadius, loading/empty states, accessibility (ARIA labels, roles), marker rendering, error boundary
  • globals.css — Leaflet dark-theme CSS overrides for popup, zoom controls, attribution styling
    Files Created (1)
  • index.ts — Barrel exports for all map components, utils, and types
    Key Features
  • Interactive markers: Hover states via eventHandlers — brighter fill, lighter border, +3px radius
  • Popup UX: Dark-themed card-style popup with hover/focusable stream items, onStreamSelect callback
  • Loading: Skeleton on dynamic import + overlay spinner when isLoading prop is set
  • Empty state: Globe icon + "No fundable streams to display" message
  • Error state: Catches render errors, shows retry button
  • Accessibility: role="application", role="button", aria-label on markers, role="status" on loading/legend, keyboard-navigable popup items

Summary by CodeRabbit

  • New Features

    • Added an interactive worldwide map for viewing fundable streams.
    • Added clustered, status-colored markers with selectable stream details.
    • Added filtering by status, category, and search text.
    • Added loading, empty, and error states with retry support.
    • Added an accessible dark-themed interface and status legend.
  • Tests

    • Added coverage for map rendering, filtering, clustering, accessibility, and error handling.

@drips-wave

drips-wave Bot commented Jul 29, 2026

Copy link
Copy Markdown

@RemmyAcee Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@RemmyAcee

Copy link
Copy Markdown
Author

Done, Close: #567

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds an interactive Leaflet map for fundable streams with typed data, grid clustering, filtering, popups, loading and error states, dark styling, public exports, and Vitest/React Testing Library coverage.

Changes

Fundable map feature

Layer / File(s) Summary
Map contracts and stream processing
apps/web/src/components/map/types.ts, apps/web/src/components/map/cluster-utils.ts, apps/web/src/components/map/index.ts, apps/web/package.json
Defines public stream/map types, exports clustering and filtering helpers, and adds Leaflet dependencies.
Leaflet map rendering
apps/web/src/components/map/FundableMapView.tsx
Renders dark tiles, fitted map bounds, clustered markers, interactive popups, and stream selection callbacks.
Map loading, filtering, and error states
apps/web/src/components/map/FundableMap.tsx
Adds client-only loading, filter handling, status counts, empty states, and error-boundary recovery.
Map styling and validation
apps/web/src/app/globals.css, apps/web/src/components/map/FundableMap.test.tsx
Adds Leaflet dark-theme styles and tests for utilities, rendering, accessibility, markers, loading, and errors.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant FundableMap
  participant FundableMapView
  participant Leaflet
  participant PopupItem
  FundableMap->>FundableMapView: pass filtered streams
  FundableMapView->>Leaflet: render tiles and cluster markers
  FundableMapView->>Leaflet: fit bounds to stream coordinates
  Leaflet->>PopupItem: display cluster stream actions
  PopupItem->>FundableMap: select a stream
Loading

Suggested reviewers: nupedev

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check ❓ Inconclusive The implementation covers the map, clustering, states, accessibility, interactions, and tests, but viewport coverage, disabled states, and test execution are not verifiable. Provide evidence that mobile, tablet, and desktop layouts work, interactive elements have disabled states, and the automated test command passes.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the primary change: an interactive Leaflet map for global fundable streams.
Out of Scope Changes check ✅ Passed The changes support the linked issue by adding the map, styling, utilities, accessibility behavior, state handling, exports, and automated tests.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
apps/web/src/components/map/FundableMap.test.tsx (1)

1-529: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Missing coverage for popup-item selection (click/keyboard).

None of the 54 tests exercise clicking or pressing Enter/Space on a PopupItem to verify onStreamSelect fires — this is one of the PR's stated accessibility features ("keyboard-accessible popup items"). Consider rendering a single-stream cluster's popup content and firing click/keyDown on the resulting button, asserting the selection callback is invoked.

🤖 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 `@apps/web/src/components/map/FundableMap.test.tsx` around lines 1 - 529, Add
coverage in the FundableMapView rendering tests for PopupItem selection: render
a single-stream cluster with an onStreamSelect callback, then trigger click and
Enter/Space keyDown events on the popup item’s accessible button and assert the
callback receives the stream. Use the existing mock stream data and
testing-library queries, preserving the current rendering and accessibility
assertions.
apps/web/src/components/map/FundableMapView.tsx (1)

218-226: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Redundant isMounted effect gate (also flagged by static analysis).

FundableMapView is only ever mounted client-side via next/dynamic(() => import("./FundableMapView"), { ssr: false }) in FundableMap.tsx. The useEffect+setIsMounted gate here is therefore redundant for that path, and calling setState synchronously in an effect on mount triggers an extra render (brief flash to null) as well as the ESLint react-hooks/set-state-in-effect error.

♻️ Suggested simplification
-  const [isMounted, setIsMounted] = useState(false);
-
-  useEffect(() => {
-    setIsMounted(true);
-  }, []);
-
-  const clusters = useMemo(() => clusterStreams(streams), [streams]);
-
-  if (!isMounted) return null;
+  const clusters = useMemo(() => clusterStreams(streams), [streams]);

If this component may be rendered directly (bypassing the ssr:false wrapper) elsewhere, keep the guard but avoid the effect (e.g. lazy useState initializer checking typeof window).

🤖 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 `@apps/web/src/components/map/FundableMapView.tsx` around lines 218 - 226,
Remove the redundant isMounted state, its useEffect, and the early null return
from FundableMapView, since the component is loaded through the ssr:false
dynamic wrapper. Preserve the clusters useMemo and normal render path; only
retain an equivalent non-effect client check if direct server-rendered usage
exists elsewhere.

Source: Linters/SAST tools

🤖 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 `@apps/web/src/components/map/FundableMap.test.tsx`:
- Around line 516-528: Update the “error boundary” test so the defined
ThrowingComponent is actually rendered through the FundableMap error-boundary
path, then assert the expected fallback UI rather than the normal application
role. Keep the test focused on verifying that a render error is caught and the
fallback is displayed.

In `@apps/web/src/components/map/FundableMap.tsx`:
- Around line 108-151: Update FundableMap so filteredStreams uses activeFilters
rather than the raw filters prop, ensuring internal filter state affects
rendered markers. Compute statusCounts from filteredStreams so the legend
matches visible results. Locate the intended search/status/category filter UI
and render or connect it within FundableMap so handleFilterChange is reachable;
if it belongs in a parent, document and wire that ownership through the existing
props without adding duplicate controls.

In `@apps/web/src/components/map/FundableMapView.tsx`:
- Around line 181-196: Remove the ineffective accessibility props from the
CircleMarker in the cluster-marker rendering path, or replace them with a
genuine focusable overlay that handles Tab and Enter/Space activation. Do not
leave role="button", tabIndex, or aria-label on CircleMarker unless keyboard
focus and activation are implemented on an actual DOM element.

---

Nitpick comments:
In `@apps/web/src/components/map/FundableMap.test.tsx`:
- Around line 1-529: Add coverage in the FundableMapView rendering tests for
PopupItem selection: render a single-stream cluster with an onStreamSelect
callback, then trigger click and Enter/Space keyDown events on the popup item’s
accessible button and assert the callback receives the stream. Use the existing
mock stream data and testing-library queries, preserving the current rendering
and accessibility assertions.

In `@apps/web/src/components/map/FundableMapView.tsx`:
- Around line 218-226: Remove the redundant isMounted state, its useEffect, and
the early null return from FundableMapView, since the component is loaded
through the ssr:false dynamic wrapper. Preserve the clusters useMemo and normal
render path; only retain an equivalent non-effect client check if direct
server-rendered usage exists elsewhere.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 22b480a2-7f82-46cb-b83a-f9006b40bd1e

📥 Commits

Reviewing files that changed from the base of the PR and between 375c936 and da6b08f.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (8)
  • apps/web/package.json
  • apps/web/src/app/globals.css
  • apps/web/src/components/map/FundableMap.test.tsx
  • apps/web/src/components/map/FundableMap.tsx
  • apps/web/src/components/map/FundableMapView.tsx
  • apps/web/src/components/map/cluster-utils.ts
  • apps/web/src/components/map/index.ts
  • apps/web/src/components/map/types.ts

Comment thread apps/web/src/components/map/FundableMap.test.tsx
Comment thread apps/web/src/components/map/FundableMap.tsx
Comment thread apps/web/src/components/map/FundableMapView.tsx
@Idrhas

Idrhas commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

dont forget to offramp using https://stellar.fundable.finance/offramp its fast, free and p2p rates

2 similar comments
@Idrhas

Idrhas commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

dont forget to offramp using https://stellar.fundable.finance/offramp its fast, free and p2p rates

@Idrhas

Idrhas commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

dont forget to offramp using https://stellar.fundable.finance/offramp its fast, free and p2p rates

@RemmyAcee

Copy link
Copy Markdown
Author

dont forget to offramp using https://stellar.fundable.finance/offramp its fast, free and p2p rates

definitely

@RemmyAcee

Copy link
Copy Markdown
Author

kindly merge

@coderabbitai coderabbitai Bot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/package.json (1)

37-43: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Restore ioredis for apps/web/src/lib/redis.ts.

apps/web/src/lib/redis.ts still imports Redis from "ioredis", while apps/web/package.json no longer provides the package outside the lockfile references. Move the Redis support elsewhere or add ioredis back to apps/web dependencies.

🤖 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 `@apps/web/package.json` around lines 37 - 43, Restore the missing ioredis
dependency for the Redis import in apps/web/src/lib/redis.ts by adding ioredis
to the dependencies in apps/web/package.json, preserving the existing Redis
implementation and aligning the package manifest with the import.
🤖 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.

Outside diff comments:
In `@apps/web/package.json`:
- Around line 37-43: Restore the missing ioredis dependency for the Redis import
in apps/web/src/lib/redis.ts by adding ioredis to the dependencies in
apps/web/package.json, preserving the existing Redis implementation and aligning
the package manifest with the import.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: df070c1a-a8f5-420f-a291-322bc76474c9

📥 Commits

Reviewing files that changed from the base of the PR and between da6b08f and 9dd2b0c.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (2)
  • apps/web/package.json
  • apps/web/src/app/globals.css
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/src/app/globals.css

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.

[Frontend] Add Interactive Leaflet Map Visualizing Global Fundable Streams

3 participants