feat: DR-7743 global live activity page#7718
Conversation
Add /global page to apps/site with real-time world map showing Prisma Accelerate traffic. Polls analytics API every 60 seconds, renders animated markers on SVG map with tooltips, legend, and social share links.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
✅ Files skipped from review due to trivial changes (2)
🚧 Files skipped from review as they are similar to previous changes (1)
WalkthroughAdds a global traffic feature: airport coordinate data, a GET API route that fetches and aligns analytics to airports, a client-side WorldMap component with periodic data fetch and animated markers, a CSS keyframes animation, and a new Global page route with hero and share links. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
- Markers start opacity:0, animate in via pulsate - Inactive markers 8px with no animation - Legend positioned absolutely: bottom-left on desktop, centered below on mobile - Map padding/margins matching original CSS - Button row responsive: column on mobile, row on desktop - Footnote spacing matching website
|
The latest updates on your projects. Learn more about Argos notifications ↗︎
|
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (6)
apps/site/src/data/airports-code.ts (1)
1-8: Consider exporting a TypeScript type for the airport data shape.Adding an explicit type export would improve type safety and reusability when this data is consumed elsewhere in the codebase, such as in
route.ts.📝 Proposed type export
+export type Airport = { + pop: string; + coordinates: { + lon: number; + lat: number; + }; +}; + -export const airports = [ +export const airports: Airport[] = [ { pop: "ACC", coordinates: {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/site/src/data/airports-code.ts` around lines 1 - 8, The airports array currently has no explicit TypeScript type export; add and export a descriptive interface (e.g., Airport or AirportData) that defines the shape: a string code property (pop or better named code), and a coordinates object with lon and lat as numbers, then annotate and export the airports constant with this type (export const airports: Airport[] = ...). Update any consumers (like route.ts) to import the new Airport type when needed to improve type safety and reuse.apps/site/src/app/global/_components/world-map.tsx (3)
84-86: Preferdata.popas the React key instead of array index.Each airport has a unique
pop(IATA code), which is a stable identifier. Using it as the key is more semantically correct and helps React optimize reconciliation if the array order ever changes.🔑 Use stable key
{points.map((data, idx) => ( - <Marker key={idx} data={data} /> + <Marker key={data.pop} data={data} /> ))}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/site/src/app/global/_components/world-map.tsx` around lines 84 - 86, The map over points uses the array index as the React key which is unstable; change the key on the Marker component in the points.map call from key={idx} to key={data.pop} (or key={`${data.pop}-${idx}`} if you need a safe fallback when pop might be missing) so each Marker uses the airport's stable IATA identifier; update the points.map JSX where Marker is rendered to use that property instead of idx.
59-61: ThedataPointsref is unused and can be removed.The ref
dataPointsis assigned on line 69 but never read anywhere. Only thepointsstate is used for rendering. This is dead code that adds confusion.🧹 Remove unused ref
export function WorldMap() { - const dataPoints = useRef<DataPoint[]>([]); const [points, setPoints] = useState<DataPoint[]>([]); useEffect(() => { const fetchData = async () => { try { const response = await fetch("/api/worldmap"); if (!response.ok) return; const data: DataPoint[] = await response.json(); - dataPoints.current = data; setPoints(data); } catch {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/site/src/app/global/_components/world-map.tsx` around lines 59 - 61, The WorldMap component declares an unused ref dataPoints (useRef<DataPoint[]>) which is assigned but never read; remove the dead ref and any assignments to it so rendering only uses the points state and setPoints. Specifically, delete the dataPoints variable declaration in WorldMap and remove any lines that write to dataPoints.current, leaving points and setPoints as the sole source of truth for map rendering.
28-31: Consider clamping the marker size to prevent extremely large markers.If
ratiois a very large value (e.g., 10), the marker width/height would be20 * 11 = 220px, which could obscure other markers and look visually broken. A max size limit would make the UI more robust.📏 Proposed size clamping
...(isActive && { - width: `${20 * (1 + (data.ratio || 0))}px`, - height: `${20 * (1 + (data.ratio || 0))}px`, + width: `${Math.min(20 * (1 + (data.ratio || 0)), 60)}px`, + height: `${Math.min(20 * (1 + (data.ratio || 0)), 60)}px`, }),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/site/src/app/global/_components/world-map.tsx` around lines 28 - 31, The marker sizing logic using isActive and data.ratio can produce excessively large markers; replace the raw calculation in the block that sets width/height (the code using width: `${20 * (1 + (data.ratio || 0))}px`) with a clamped value: compute baseSize = 20 * (1 + (data.ratio || 0)), then clamp it to a reasonable min/max (e.g., Math.max(minSize, Math.min(baseSize, maxSize))) and use that clampedSize for both width and height; update the code in world-map.tsx where isActive and data.ratio are used to derive marker size so oversized markers are bounded.apps/site/src/app/api/worldmap/route.ts (2)
51-58: RedundanttransformCoordinatescalls for active airports.Active airports already have their
cured_coordcomputed incured_data(line 46), but line 55 recomputes it. You could reuse the pre-transformed coordinates from the matchingcured_dataentry instead.♻️ Proposed optimization to avoid redundant transformations
const cured_airport_data = airports.map((airport) => { const active = cured_data.find((d) => d?.pop === airport.pop); return { pop: airport.pop, - cured_coord: transformCoordinates(airport.coordinates), + cured_coord: active?.cured_coord ?? transformCoordinates(airport.coordinates), ...(active && { ratio: active.ratio }), }; });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/site/src/app/api/worldmap/route.ts` around lines 51 - 58, The map that builds cured_airport_data recomputes transformCoordinates for airports that already have a precomputed cured_coord in cured_data; update the mapping logic in the cured_airport_data computation so that for each airport you find active = cured_data.find(...) and then set cured_coord to active.cured_coord when active exists, otherwise call transformCoordinates(airport.coordinates); keep the rest of the object shape (pop and optional ratio) unchanged and preserve existing null/undefined checks around active.
7-24: The coordinate zero-check is unnecessarily complex.The condition
coordinates.lon > 0 || coordinates.lon < 0is mathematically equivalent tocoordinates.lon !== 0. The same applies to latitude. This makes the intent harder to understand at a glance.✨ Simplified coordinate transformation
function transformCoordinates(coordinates: { lat: number; lon: number }) { - let temp_lon = coordinates.lon; - if (coordinates.lon > 0 || coordinates.lon < 0) { - temp_lon = temp_lon + 180; - } else temp_lon = 180; - temp_lon = (temp_lon * 100) / 360; + // Normalize longitude from [-180, 180] to [0, 100] percentage + const temp_lon = ((coordinates.lon + 180) * 100) / 360; - let temp_lat = coordinates.lat; - if (coordinates.lat > 0 || coordinates.lat < 0) { - temp_lat = temp_lat * -1 + 90; - } else temp_lat = 90; - temp_lat = (temp_lat * 100) / 180; + // Normalize latitude from [-90, 90] to [0, 100] percentage (inverted for CSS top) + const temp_lat = ((90 - coordinates.lat) * 100) / 180; return { lon: Number(temp_lon.toFixed(2)), lat: Number(temp_lat.toFixed(2)), }; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/site/src/app/api/worldmap/route.ts` around lines 7 - 24, The conditionals in transformCoordinates use `coordinates.lon > 0 || coordinates.lon < 0` and the same for lat which is equivalent to `!== 0`; update those checks to `coordinates.lon !== 0` and `coordinates.lat !== 0` (preserving the existing branches: add 180 when lon !== 0 else set 180, and invert/add 90 when lat !== 0 else set 90) to make the intent clear and simplify the code while keeping the same numeric behavior and returned values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/site/src/app/api/worldmap/route.ts`:
- Around line 60-64: The Cache-Control on the NextResponse in route.ts is set to
s-maxage=86400 which prevents CDN refresh for 24h and conflicts with the client
polling every ~60s; update the headers passed to NextResponse.json (the return
block) to use a much shorter s-maxage (e.g., s-maxage=60 or s-maxage=120) and
adjust stale-while-revalidate appropriately (e.g., stale-while-revalidate=30) so
the CDN TTL matches the live polling interval.
- Around line 28-35: The external fetch to ACCELERATE_ANALYTICS in route.ts
needs a timeout to avoid hanging; wrap the fetch in an AbortController (set a
5–10s timeout via setTimeout that calls controller.abort()), pass
controller.signal to the fetch call, clear the timeout after fetch completes,
and catch AbortError to return a bounded response (e.g., NextResponse.json with
a 504/timeout message). Update the fetch invocation that currently uses
fetch(ACCELERATE_ANALYTICS) to accept the controller.signal and add the
abort/cleanup logic around it.
In `@apps/site/src/data/airports-code.ts`:
- Around line 1843-1849: The IAD airport entry (object with pop: "IAD" and
coordinates.lon: -77.455833) has an incorrect latitude of 48.944444; update the
coordinates.lat to the correct Washington Dulles value (approximately 38.95) so
the marker renders in the proper location on the map.
---
Nitpick comments:
In `@apps/site/src/app/api/worldmap/route.ts`:
- Around line 51-58: The map that builds cured_airport_data recomputes
transformCoordinates for airports that already have a precomputed cured_coord in
cured_data; update the mapping logic in the cured_airport_data computation so
that for each airport you find active = cured_data.find(...) and then set
cured_coord to active.cured_coord when active exists, otherwise call
transformCoordinates(airport.coordinates); keep the rest of the object shape
(pop and optional ratio) unchanged and preserve existing null/undefined checks
around active.
- Around line 7-24: The conditionals in transformCoordinates use
`coordinates.lon > 0 || coordinates.lon < 0` and the same for lat which is
equivalent to `!== 0`; update those checks to `coordinates.lon !== 0` and
`coordinates.lat !== 0` (preserving the existing branches: add 180 when lon !==
0 else set 180, and invert/add 90 when lat !== 0 else set 90) to make the intent
clear and simplify the code while keeping the same numeric behavior and returned
values.
In `@apps/site/src/app/global/_components/world-map.tsx`:
- Around line 84-86: The map over points uses the array index as the React key
which is unstable; change the key on the Marker component in the points.map call
from key={idx} to key={data.pop} (or key={`${data.pop}-${idx}`} if you need a
safe fallback when pop might be missing) so each Marker uses the airport's
stable IATA identifier; update the points.map JSX where Marker is rendered to
use that property instead of idx.
- Around line 59-61: The WorldMap component declares an unused ref dataPoints
(useRef<DataPoint[]>) which is assigned but never read; remove the dead ref and
any assignments to it so rendering only uses the points state and setPoints.
Specifically, delete the dataPoints variable declaration in WorldMap and remove
any lines that write to dataPoints.current, leaving points and setPoints as the
sole source of truth for map rendering.
- Around line 28-31: The marker sizing logic using isActive and data.ratio can
produce excessively large markers; replace the raw calculation in the block that
sets width/height (the code using width: `${20 * (1 + (data.ratio || 0))}px`)
with a clamped value: compute baseSize = 20 * (1 + (data.ratio || 0)), then
clamp it to a reasonable min/max (e.g., Math.max(minSize, Math.min(baseSize,
maxSize))) and use that clampedSize for both width and height; update the code
in world-map.tsx where isActive and data.ratio are used to derive marker size so
oversized markers are bounded.
In `@apps/site/src/data/airports-code.ts`:
- Around line 1-8: The airports array currently has no explicit TypeScript type
export; add and export a descriptive interface (e.g., Airport or AirportData)
that defines the shape: a string code property (pop or better named code), and a
coordinates object with lon and lat as numbers, then annotate and export the
airports constant with this type (export const airports: Airport[] = ...).
Update any consumers (like route.ts) to import the new Airport type when needed
to improve type safety and reuse.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 9dc6a216-83a7-490e-86e4-86664184ce33
⛔ Files ignored due to path filters (1)
apps/site/public/illustrations/world-map/map.svgis excluded by!**/*.svg
📒 Files selected for processing (5)
apps/site/src/app/api/worldmap/route.tsapps/site/src/app/global.cssapps/site/src/app/global/_components/world-map.tsxapps/site/src/app/global/page.tsxapps/site/src/data/airports-code.ts
| const response = await fetch(ACCELERATE_ANALYTICS); | ||
|
|
||
| if (!response.ok) { | ||
| return NextResponse.json( | ||
| { message: "Error fetching analytics" }, | ||
| { status: response.status }, | ||
| ); | ||
| } |
There was a problem hiding this comment.
External fetch lacks a timeout, risking request hangs.
If the analytics endpoint becomes slow or unresponsive, this request will hang indefinitely. Consider adding an AbortController with a reasonable timeout (e.g., 5-10 seconds) to ensure the API route responds in a bounded time.
⏱️ Proposed timeout implementation
export async function GET() {
try {
- const response = await fetch(ACCELERATE_ANALYTICS);
+ const controller = new AbortController();
+ const timeoutId = setTimeout(() => controller.abort(), 10000);
+
+ const response = await fetch(ACCELERATE_ANALYTICS, {
+ signal: controller.signal,
+ });
+ clearTimeout(timeoutId);
if (!response.ok) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/site/src/app/api/worldmap/route.ts` around lines 28 - 35, The external
fetch to ACCELERATE_ANALYTICS in route.ts needs a timeout to avoid hanging; wrap
the fetch in an AbortController (set a 5–10s timeout via setTimeout that calls
controller.abort()), pass controller.signal to the fetch call, clear the timeout
after fetch completes, and catch AbortError to return a bounded response (e.g.,
NextResponse.json with a 504/timeout message). Update the fetch invocation that
currently uses fetch(ACCELERATE_ANALYTICS) to accept the controller.signal and
add the abort/cleanup logic around it.
| return NextResponse.json(cured_airport_data, { | ||
| headers: { | ||
| "Cache-Control": "s-maxage=86400, stale-while-revalidate=59", | ||
| }, | ||
| }); |
There was a problem hiding this comment.
Cache duration of 24 hours conflicts with "live" data polling.
The s-maxage=86400 (24 hours) means CDN-cached responses won't refresh for a full day, which contradicts the client polling every 60 seconds and the "live activity" premise. If live data is important, consider reducing s-maxage to match the polling interval or slightly longer (e.g., s-maxage=60 or s-maxage=120).
🔄 Proposed cache header adjustment
return NextResponse.json(cured_airport_data, {
headers: {
- "Cache-Control": "s-maxage=86400, stale-while-revalidate=59",
+ "Cache-Control": "s-maxage=60, stale-while-revalidate=30",
},
});🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/site/src/app/api/worldmap/route.ts` around lines 60 - 64, The
Cache-Control on the NextResponse in route.ts is set to s-maxage=86400 which
prevents CDN refresh for 24h and conflicts with the client polling every ~60s;
update the headers passed to NextResponse.json (the return block) to use a much
shorter s-maxage (e.g., s-maxage=60 or s-maxage=120) and adjust
stale-while-revalidate appropriately (e.g., stale-while-revalidate=30) so the
CDN TTL matches the live polling interval.
| { | ||
| pop: "IAD", | ||
| coordinates: { | ||
| lon: -77.455833, | ||
| lat: 48.944444, | ||
| }, | ||
| }, |
There was a problem hiding this comment.
IAD (Washington Dulles) latitude appears incorrect.
The latitude 48.944444 would place this marker in Canada, roughly near the US-Canada border. Washington Dulles International Airport is actually located at approximately latitude 38.95. This data error will cause the marker to render in the wrong geographic position on the map.
🗺️ Proposed coordinate fix
{
pop: "IAD",
coordinates: {
lon: -77.455833,
- lat: 48.944444,
+ lat: 38.944444,
},
},📝 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.
| { | |
| pop: "IAD", | |
| coordinates: { | |
| lon: -77.455833, | |
| lat: 48.944444, | |
| }, | |
| }, | |
| { | |
| pop: "IAD", | |
| coordinates: { | |
| lon: -77.455833, | |
| lat: 38.944444, | |
| }, | |
| }, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/site/src/data/airports-code.ts` around lines 1843 - 1849, The IAD
airport entry (object with pop: "IAD" and coordinates.lon: -77.455833) has an
incorrect latitude of 48.944444; update the coordinates.lat to the correct
Washington Dulles value (approximately 38.95) so the marker renders in the
proper location on the map.
Summary
/globalpage toapps/siteshowing real-time Prisma Accelerate traffic on a world map/api/worldmapproxies analytics from the Prisma analytics exporter, transforms coordinates, and merges with airport POP dataSummary by CodeRabbit
New Features
Style