Skip to content

feat: add Satellite of the Day spotlight card - #148

Open
draexon wants to merge 1 commit into
7-Blocks:mainfrom
draexon:feature/satellite-of-the-day
Open

feat: add Satellite of the Day spotlight card#148
draexon wants to merge 1 commit into
7-Blocks:mainfrom
draexon:feature/satellite-of-the-day

Conversation

@draexon

@draexon draexon commented Jul 27, 2026

Copy link
Copy Markdown

User description

Summary

Adds a "Satellite of the Day" section to the dashboard, as requested in #143.

  • A curated dataset of 6 notable satellites (ISS, Hubble, Terra, Aqua, NOAA-19, Landsat 8) with mission summary, operator, launch date, and facts. This is necessary since the backend only stores orbital mechanics (TLE data), not mission/bio content.
  • A hook that deterministically rotates the featured satellite once every 24 hours, plus a "Show Another" button for an on-demand preview.
  • Orbit type, altitude, inclination, and period are pulled live from the real catalog by cross-referencing each satellite's NORAD catalog number via api.getCatalogObjectByNorad, not hardcoded.
  • "View on Globe" flies the camera to the satellite and opens its info panel. This required extending EarthTwin with a forwardRef/imperative handle (flyToSatellite), and adding an external focusRequest prop to SpotlightManager so it can lock selection from outside a globe click.
  • Loading skeleton and a graceful fallback state if a satellite isn't found in the live catalog (e.g. re-entered/decayed).
  • Responsive layout that stacks vertically on narrow/mobile widths.

Related Issue

Closes #143

Type of Change

  • ✨ New feature
  • 🎨 UI/UX enhancement

Screenshots / Screen Recordings

(attach your 3 images here)

Note: screenshots show the fallback state since I tested without the backend running locally. Orbit stats query /catalog/objects/{norad} live and populate normally once the backend is up. The fallback is the correctly working "no data" path, not a missing feature.

Testing Performed

  • Tested locally
  • Tested relevant functionality
  • Checked for linting errors
  • Checked for TypeScript/build errors
  • Tested responsive behavior (if applicable)

Breaking Changes

None. All changes are additive: new files, an optional new prop on SpotlightManager, and a ref-based imperative method on EarthTwin that doesn't alter existing behavior.

Checklist

  • I have read and followed the contributing guidelines.
  • My code follows the project's coding standards and guidelines.
  • I have completed testing of my changes.
  • I have updated documentation where applicable.
  • I have checked that my changes do not introduce unintended regressions.

ECSoC26 Submission

  • ECSoC26-L1 – Beginner
Screenshot 2026-07-28 000112 Screenshot 2026-07-28 000145 Screenshot 2026-07-28 000226

CodeAnt-AI Description

Add a daily Satellite of the Day spotlight to the dashboard

What Changed

  • The dashboard now features a curated satellite that changes each day, with mission details, operator, launch date, status, and notable facts.
  • Live orbital data shows the satellite’s orbit type, altitude, inclination, and period when available.
  • Users can select another satellite or return to the daily pick.
  • “View on Globe” flies to the satellite and opens its information panel; unavailable catalog data disables the action and explains why.
  • The spotlight adapts to narrow screens and shows loading and unavailable-data states.

Impact

✅ Daily satellite discovery
✅ Live orbital details
✅ Direct navigation from dashboard to globe
✅ Clear unavailable-data messaging

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

Summary by CodeRabbit

  • New Features
    • Added a “Satellite of the Day” experience with mission details, status, imagery, and live orbit information.
    • Added “Show Another” to browse featured satellites and return to the daily selection.
    • Added orbit metrics including altitude, inclination, period, and orbit classification.
    • Added “View on Globe” to fly directly to a featured satellite and highlight it.
  • Bug Fixes
    • Added loading, unavailable-data, and missing-image states for more reliable satellite details.

Greptile Summary

Adds a curated daily satellite spotlight and connects its live orbital data to the dashboard globe.

  • Introduces deterministic daily and manual satellite selection.
  • Fetches live catalog data for spotlight statistics and fallback handling.
  • Adds imperative EarthTwin navigation and externally requested spotlight selection.
  • Inserts the responsive spotlight card between the globe and dashboard KPIs.

Confidence Score: 2/5

The PR should not merge until globe navigation handles catalog-map misses, reveals the target section, and the daily selection updates across midnight.

The enabled navigation action can silently do nothing because its availability check and EarthTwin use different catalog scopes, successful off-screen navigation has no visible result, and the daily pick remains stale in overnight sessions.

Files Needing Attention: frontend/src/components/EarthTwin.tsx, frontend/src/hooks/useSatelliteOfTheDay.ts, frontend/src/pages/Dashboard.tsx

Important Files Changed

Filename Overview
frontend/src/components/EarthTwin.tsx Adds imperative camera navigation, but silently rejects objects missing from EarthTwin's truncated catalog map.
frontend/src/components/SatelliteOfTheDay/SpotlightCard.tsx Adds the live-data spotlight UI, while enabling navigation based on a data source that does not guarantee globe availability.
frontend/src/components/SatelliteSpotlight/SpotlightManager.tsx Adds externally requested selection using the existing selection path without an independent defect.
frontend/src/hooks/useSatelliteOfTheDay.ts Adds daily/manual rotation, but freezes the daily date for the component's entire mounted lifetime.
frontend/src/pages/Dashboard.tsx Integrates the spotlight and globe ref, but does not reveal the globe when navigation is triggered from a scrolled card.
frontend/src/constants/spotlightSatellites.ts Adds the six-entry curated editorial dataset and NORAD mappings.

Sequence Diagram

sequenceDiagram
  participant U as User
  participant C as SpotlightCard
  participant API as Catalog API
  participant D as Dashboard
  participant E as EarthTwin
  participant S as SpotlightManager
  C->>API: getCatalogObjectByNorad(NORAD)
  API-->>C: Live orbital object
  U->>C: View on Globe
  C->>D: onViewOnGlobe(NORAD)
  D->>E: flyToSatellite(NORAD)
  E->>E: Lookup object in loaded catalog map
  alt Object is loaded
    E->>E: Fly Cesium camera
    E->>S: focusRequest
    S->>S: Select object and open panel
  else Object is absent from first page
    E-->>D: Return without visible action
  end
Loading
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
frontend/src/components/EarthTwin.tsx:354
**Catalog scope breaks globe navigation**

When the direct NORAD lookup succeeds for an object outside EarthTwin's first 500 catalog entries, the button is enabled but `flyToSatellite` silently returns because the object is absent from `catalogMapRef`, causing no camera movement, selection, panel, or error feedback.

### Issue 2 of 3
frontend/src/hooks/useSatelliteOfTheDay.ts:31-34
**Daily selection freezes at mount**

When Dashboard remains mounted across local midnight, the empty dependency array prevents `todayKey()` from being evaluated again, causing the previous day's satellite to remain selected until Dashboard remounts; resetting to Today's Pick also restores that stale index.

### Issue 3 of 3
frontend/src/pages/Dashboard.tsx:128
**Globe navigation remains off-screen**

When the card is visible after the preceding globe section has scrolled outside MainLayout's overflow viewport, this callback changes only Cesium camera and selection state without revealing the globe, causing the user to see no response to View on Globe.

Reviews (1): Last reviewed commit: "feat: add Satellite of the Day spotlight..." | Re-trigger Greptile

Greptile also left 3 inline comments on this PR.

@codeant-ai

codeant-ai Bot commented Jul 27, 2026

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Reviewed your PR 351beab Jul 27, 2026 · 18:43 18:45

@github-actions github-actions Bot added bug Something isn't working documentation Improvements or additions to documentation enhancement New feature or request frontend Frontend development size/L Large contribution requiring significant work. size:L Large size type:bug Fixes an existing bug or unexpected behavior. type:documentation Improves project documentation. type:feature Introduces a new feature or enhancement. type:frontend Changes frontend or client-side code. ECSoC26 Official label for ECSoC26 event contributions. ECSoC26-L1 Level 1 contribution for the ECSoC26 event. labels Jul 27, 2026
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The dashboard gains a “Satellite of the Day” card with curated metadata, live orbital details, daily selection, and globe navigation. EarthTwin exposes an imperative satellite-focus API that updates the camera and SpotlightManager selection.

Changes

Satellite of the Day spotlight

Layer / File(s) Summary
Daily satellite data and card
frontend/src/constants/spotlightSatellites.ts, frontend/src/hooks/useSatelliteOfTheDay.ts, frontend/src/components/SatelliteOfTheDay/SpotlightCard.tsx
Adds curated satellite metadata, deterministic daily rotation, manual alternate selection, live orbit retrieval, loading and fallback states, orbit metrics, and the “VIEW ON GLOBE” action.
Imperative globe focus
frontend/src/components/EarthTwin.tsx, frontend/src/components/SatelliteSpotlight/SpotlightManager.tsx
Adds EarthTwinHandle.flyToSatellite, reduced-motion-aware camera movement, and nonce-based focus requests that select the matching satellite in SpotlightManager.
Dashboard spotlight integration
frontend/src/pages/Dashboard.tsx
Attaches a typed EarthTwin ref and routes the spotlight card’s globe action through the imperative API.

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

Possibly related PRs

  • 7-Blocks/Kepler#142: Both changes extend programmatic satellite spotlight selection through EarthTwin and SpotlightManager.
  • 7-Blocks/Kepler#147: Both changes modify EarthTwin camera navigation and satellite selection behavior.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately reflects the main change: adding a Satellite of the Day spotlight card.
Description check ✅ Passed The description follows the template and includes summary, related issue, type, screenshots, testing, breaking changes, checklist, and ECSoC26 selection.
Linked Issues check ✅ Passed The changes satisfy #143 by adding the daily spotlight, metadata, orbit stats, Show Another, View on Globe, fallback states, and responsive behavior.
Out of Scope Changes check ✅ Passed The modified files are all directly related to the Satellite of the Day feature and its dashboard/globe integrations.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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.

Warning

⚠️ This pull request shows signs of AI-generated slop (ai_padded_prose). It has been flagged by CodeRabbit slop detection and should be reviewed carefully.

@github-actions github-actions Bot added the AI Artificial Intelligence and Machine Learning label Jul 27, 2026
Comment on lines +351 to +354
const flyToSatellite = useCallback((catalogNumber: string) => {
const viewer = viewerRef.current;
const obj = catalogMapRef.current.get(catalogNumber);
if (!viewer || viewer.isDestroyed() || !obj) return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: When the card is clicked before Cesium finishes loading its catalog, this early return discards the request entirely. Since focusRequest is only set after this check and the later catalog refresh does not replay it, the same valid click silently does nothing and cannot select the satellite once the data becomes available. Record the request before checking readiness, then consume it when the viewer and catalog are ready. [race condition]

Severity Level: Major ⚠️
- ❌ Initial VIEW ON GLOBE clicks silently do nothing.
- ⚠️ Dashboard spotlight navigation requires another click.
- ⚠️ Users receive no readiness feedback or retry.
Steps of Reproduction ✅
1. Open the Dashboard, which renders the Satellite of the Day card at
frontend/src/pages/Dashboard.tsx:121-128 alongside the EarthTwin component.

2. Before Cesium initialization and catalog loading finish, click VIEW ON GLOBE; the card
invokes earthTwinRef.current?.flyToSatellite() at frontend/src/pages/Dashboard.tsx:128.

3. EarthTwin.flyToSatellite() at frontend/src/components/EarthTwin.tsx:351-354 sees either
a null viewer or an empty catalogMapRef because initialization occurs asynchronously at
lines 267-321, then returns without calling setFocusRequest().

4. Catalog loading later completes in populateEntities() at
frontend/src/components/EarthTwin.tsx:153-246, but no focus request was recorded, so
SpotlightManager never selects the requested satellite and the original click has no
effect.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** frontend/src/components/EarthTwin.tsx
**Line:** 351:354
**Comment:**
	*Race Condition: When the card is clicked before Cesium finishes loading its catalog, this early return discards the request entirely. Since `focusRequest` is only set after this check and the later catalog refresh does not replay it, the same valid click silently does nothing and cannot select the satellite once the data becomes available. Record the request before checking readiness, then consume it when the viewer and catalog are ready.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +356 to +360
const pos = keplerToLatLonAlt(obj);
if (pos) {
const destination = Cesium.Cartesian3.fromDegrees(pos.lon, pos.lat, pos.alt * 1000 + 2000000);
viewer.camera.flyTo({ destination, duration: prefersReducedMotion() ? 0 : 1.5 });
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: SpotlightCard enables this action when only semimajor axis, inclination, and period are present, but keplerToLatLonAlt also requires RAAN, argument of perigee, mean anomaly, and mean motion. For a catalog object missing any of those fields, the button opens the selection panel but does not move the camera, contradicting the View on Globe contract. The action should either use the same validity criteria as the card or provide a fallback camera position. [api mismatch]

Severity Level: Major ⚠️
- ❌ VIEW ON GLOBE fails to reposition for incomplete orbits.
- ⚠️ Spotlight selection opens at the current camera location.
- ⚠️ Card validation disagrees with globe positioning requirements.
Steps of Reproduction ✅
1. On Dashboard, select a curated satellite whose live catalog response contains
semimajor_axis, inclination, and period but a null RAAN, argument of perigee, mean
anomaly, or mean motion; these fields are nullable in
frontend/src/types/satellite.ts:7-14.

2. SpotlightCard enables VIEW ON GLOBE because summarizeOrbit() only validates
semimajor_axis, inclination, and period at
frontend/src/components/SatelliteOfTheDay/SpotlightCard.tsx:19-33, and canViewOnGlobe is
based on that result at line 71.

3. Click VIEW ON GLOBE; Dashboard.tsx:128 invokes EarthTwin.flyToSatellite(), which
retrieves the object and calls keplerToLatLonAlt() at
frontend/src/components/EarthTwin.tsx:351-356.

4. keplerToLatLonAlt() returns null at frontend/src/components/EarthTwin.tsx:52-56 because
one required orbital element is missing, so the camera.flyTo() call at lines 357-360 is
skipped even though the button was enabled; setFocusRequest() still runs at line 364 and
opens selection without moving the camera.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** frontend/src/components/EarthTwin.tsx
**Line:** 356:360
**Comment:**
	*Api Mismatch: `SpotlightCard` enables this action when only semimajor axis, inclination, and period are present, but `keplerToLatLonAlt` also requires RAAN, argument of perigee, mean anomaly, and mean motion. For a catalog object missing any of those fields, the button opens the selection panel but does not move the camera, contradicting the View on Globe contract. The action should either use the same validity criteria as the card or provide a fallback camera position.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +82 to +83
const obj = catalogMapRef.current?.get(focusRequest.catalogNumber) ?? null;
if (obj) selectSatellite(obj);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The external request selects any object found in catalogMapRef, but that map contains objects for which populateEntities may have skipped entity creation when keplerToLatLonAlt returned null. This can therefore display an info card and set selectedId for an object with no Cesium entity, while useSpotlightEffect cannot highlight or dim it. Verify the object exists in entitiesRef before selecting it, or defer the request until the entity map has been populated. [stale reference]

Severity Level: Major ⚠️
- ⚠️ Info card opens for an unrendered satellite.
- ⚠️ Selected globe point cannot be highlighted.
- ⚠️ Spotlight visualization becomes inconsistent with selection.
Steps of Reproduction ✅
1. Load the Dashboard and allow EarthTwin.populateEntities() to receive a catalog response
at frontend/src/components/EarthTwin.tsx:157-160.

2. For an object missing required positioning fields, populateEntities() skips entity
creation at frontend/src/components/EarthTwin.tsx:178-180 when keplerToLatLonAlt() returns
null.

3. The same object is nevertheless inserted into catalogMapRef at
frontend/src/components/EarthTwin.tsx:243-245, so catalogMapRef contains the object while
entitiesRef does not.

4. Trigger a focus request for that catalog number and observe SpotlightManager.tsx:80-85
selecting the catalog object without checking entitiesRef; useSpotlightEffect.ts:109-118
then finds no active entity to highlight, while SpotlightManager still renders
SpotlightInfoCard from lines 148-150 and reports the object as selected.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** frontend/src/components/SatelliteSpotlight/SpotlightManager.tsx
**Line:** 82:83
**Comment:**
	*Stale Reference: The external request selects any object found in `catalogMapRef`, but that map contains objects for which `populateEntities` may have skipped entity creation when `keplerToLatLonAlt` returned null. This can therefore display an info card and set `selectedId` for an object with no Cesium entity, while `useSpotlightEffect` cannot highlight or dim it. Verify the object exists in `entitiesRef` before selecting it, or defer the request until the entity map has been populated.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +31 to +34
const dailyIndex = useMemo(() => {
if (SPOTLIGHT_SATELLITES.length === 0) return 0;
return hashString(todayKey()) % SPOTLIGHT_SATELLITES.length;
}, []);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The empty dependency list evaluates todayKey() only on the initial mount. A Dashboard that remains open across local midnight retains the previous day's index indefinitely, so the documented daily rotation does not occur until the component is remounted. Track the date key in state/effect or otherwise invalidate the memo at the next local midnight. [logic error]

Severity Level: Major ⚠️
- ⚠️ Long-lived Dashboard tabs show yesterday's satellite.
- ⚠️ Daily spotlight rotation fails at local midnight.
- ⚠️ Users must remount the page to receive today's pick.
Steps of Reproduction ✅
1. Open the Dashboard, which mounts SpotlightCard at
frontend/src/pages/Dashboard.tsx:126-128; SpotlightCard calls useSatelliteOfTheDay() at
frontend/src/components/SatelliteOfTheDay/SpotlightCard.tsx:60-61.

2. At the initial mount, useSatelliteOfTheDay() computes dailyIndex once by calling
todayKey() inside the useMemo at frontend/src/hooks/useSatelliteOfTheDay.ts:31-34.

3. Keep the Dashboard tab open across local midnight; the component remains mounted and
the empty dependency list prevents the memo callback from running again.

4. After midnight, the hook continues returning activeIndex based on the previous day's
dailyIndex at frontend/src/hooks/useSatelliteOfTheDay.ts:53, so the documented daily
rotation does not occur until the Dashboard remounts.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** frontend/src/hooks/useSatelliteOfTheDay.ts
**Line:** 31:34
**Comment:**
	*Logic Error: The empty dependency list evaluates `todayKey()` only on the initial mount. A Dashboard that remains open across local midnight retains the previous day's index indefinitely, so the documented daily rotation does not occur until the component is remounted. Track the date key in state/effect or otherwise invalidate the memo at the next local midnight.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

let orbitType: OrbitSummary['orbitType'] = 'LEO';
if (altitudeKm > 2000 && altitudeKm < 35000) orbitType = 'MEO';
else if (altitudeKm >= 35000 && altitudeKm <= 36500) orbitType = 'GEO';
else if (altitudeKm > 36500) orbitType = 'HEO';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: Orbit type cannot be determined from altitude alone: every object with altitude above 36,500 km is labeled HEO, while high-altitude circular orbits and elliptical HEO orbits with a lower semimajor axis are classified incorrectly. Use eccentricity and perigee/apogee (or the application's established classification rule) before presenting this as the live orbit regime. [incorrect condition logic]

Severity Level: Major ⚠️
- ⚠️ Satellite spotlight can display incorrect orbit classifications.
- ⚠️ Live orbital metadata becomes misleading for high-altitude objects.
Steps of Reproduction ✅
1. On the dashboard, `SpotlightCard` fetches live orbital data through
`api.getCatalogObjectByNorad()` at `SpotlightCard.tsx:63-66`; the `SpaceObject` model
includes eccentricity at `services/api.ts:216-233`.

2. When the API returns an object with semimajor-axis altitude above 36,500 km,
`summarizeOrbit()` at `SpotlightCard.tsx:19-26` assigns `HEO` without examining
eccentricity, perigee, or apogee.

3. A high-altitude circular object is therefore labeled `HEO`, while an eccentric high
Earth orbit whose semimajor-axis altitude is below 36,500 km falls into `MEO` or `GEO`
instead.

4. The resulting label is rendered directly as the user-facing orbit value at
`SpotlightCard.tsx:128-132`, so the spotlight can present an incorrect orbit regime
despite using current live catalog data.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** frontend/src/components/SatelliteOfTheDay/SpotlightCard.tsx
**Line:** 26:26
**Comment:**
	*Incorrect Condition Logic: Orbit type cannot be determined from altitude alone: every object with altitude above 36,500 km is labeled `HEO`, while high-altitude circular orbits and elliptical HEO orbits with a lower semimajor axis are classified incorrectly. Use eccentricity and perigee/apogee (or the application's established classification rule) before presenting this as the live orbit regime.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +69 to +71
const liveObject = liveQuery.data?.data;
const orbit = liveObject ? summarizeOrbit(liveObject) : null;
const canViewOnGlobe = liveQuery.isSuccess && orbit !== 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.

Suggestion: The button is enabled solely from the per-object lookup, but EarthTwin builds catalogMapRef from only the first 500 catalog entries and flyToSatellite returns without action when the requested object is absent. Consequently, a successful spotlight lookup can expose an enabled “View on Globe” button that neither flies the camera nor opens the info panel. Ensure the globe can resolve the object directly or disable/report the action when it is not present in the globe dataset. [api mismatch]

Severity Level: Critical 🚨
- ❌ View-on-globe action silently does nothing for unloaded catalog objects.
- ⚠️ Spotlight navigation depends on an unrelated first-page limit.
Steps of Reproduction ✅
1. Open the dashboard, where `Dashboard.tsx:128` connects the card's `onViewOnGlobe`
callback to `earthTwinRef.current?.flyToSatellite(catalogNumber)`.

2. Let `SpotlightCard` query the selected NORAD ID through `api.getCatalogObjectByNorad()`
at `SpotlightCard.tsx:63-66`; a successful response with valid orbital fields makes
`canViewOnGlobe` true at `SpotlightCard.tsx:69-71`.

3. The globe loads only `size=500&page=1` at `EarthTwin.tsx:98-101`, then builds
`catalogMapRef` from those returned objects at `EarthTwin.tsx:144-145`. Select a valid
spotlight object that is outside this first page while its direct lookup still succeeds.

4. Click `VIEW ON GLOBE` at `SpotlightCard.tsx:164-167`. `EarthTwin.flyToSatellite()`
looks up the object at `EarthTwin.tsx:252-255` and returns when it is absent, so no camera
flight occurs and no focus request reaches `SpotlightManager`; its external selection
effect at `SpotlightManager.tsx:80-85` consequently cannot open the info panel.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** frontend/src/components/SatelliteOfTheDay/SpotlightCard.tsx
**Line:** 69:71
**Comment:**
	*Api Mismatch: The button is enabled solely from the per-object lookup, but `EarthTwin` builds `catalogMapRef` from only the first 500 catalog entries and `flyToSatellite` returns without action when the requested object is absent. Consequently, a successful spotlight lookup can expose an enabled “View on Globe” button that neither flies the camera nor opens the info panel. Ensure the globe can resolve the object directly or disable/report the action when it is not present in the globe dataset.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

const flyToSatellite = useCallback((catalogNumber: string) => {
const viewer = viewerRef.current;
const obj = catalogMapRef.current.get(catalogNumber);
if (!viewer || viewer.isDestroyed() || !obj) return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Catalog scope breaks globe navigation

When the direct NORAD lookup succeeds for an object outside EarthTwin's first 500 catalog entries, the button is enabled but flyToSatellite silently returns because the object is absent from catalogMapRef, causing no camera movement, selection, panel, or error feedback.

Prompt To Fix With AI
This is a comment left during a code review.
Path: frontend/src/components/EarthTwin.tsx
Line: 354

Comment:
**Catalog scope breaks globe navigation**

When the direct NORAD lookup succeeds for an object outside EarthTwin's first 500 catalog entries, the button is enabled but `flyToSatellite` silently returns because the object is absent from `catalogMapRef`, causing no camera movement, selection, panel, or error feedback.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +31 to +34
const dailyIndex = useMemo(() => {
if (SPOTLIGHT_SATELLITES.length === 0) return 0;
return hashString(todayKey()) % SPOTLIGHT_SATELLITES.length;
}, []);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Daily selection freezes at mount

When Dashboard remains mounted across local midnight, the empty dependency array prevents todayKey() from being evaluated again, causing the previous day's satellite to remain selected until Dashboard remounts; resetting to Today's Pick also restores that stale index.

Prompt To Fix With AI
This is a comment left during a code review.
Path: frontend/src/hooks/useSatelliteOfTheDay.ts
Line: 31-34

Comment:
**Daily selection freezes at mount**

When Dashboard remains mounted across local midnight, the empty dependency array prevents `todayKey()` from being evaluated again, causing the previous day's satellite to remain selected until Dashboard remounts; resetting to Today's Pick also restores that stale index.

How can I resolve this? If you propose a fix, please make it concise.


{/* Satellite of the Day */}
<section className="px-3 md:px-6 pt-3 md:pt-6">
<SpotlightCard onViewOnGlobe={(catalogNumber) => earthTwinRef.current?.flyToSatellite(catalogNumber)} />

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Globe navigation remains off-screen

When the card is visible after the preceding globe section has scrolled outside MainLayout's overflow viewport, this callback changes only Cesium camera and selection state without revealing the globe, causing the user to see no response to View on Globe.

Prompt To Fix With AI
This is a comment left during a code review.
Path: frontend/src/pages/Dashboard.tsx
Line: 128

Comment:
**Globe navigation remains off-screen**

When the card is visible after the preceding globe section has scrolled outside MainLayout's overflow viewport, this callback changes only Cesium camera and selection state without revealing the globe, causing the user to see no response to View on Globe.

How can I resolve this? If you propose a fix, please make it concise.

@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 `@frontend/src/components/EarthTwin.tsx`:
- Around line 351-365: Update flyToSatellite to persist the requested catalog
number when the viewer or catalog entry is unavailable, rather than returning
permanently. After populateEntities makes the viewer and catalog entry
available, consume the pending request and perform the camera flight and
setFocusRequest; preserve the existing reduced-motion behavior and avoid losing
clicks made before catalog readiness.

In `@frontend/src/components/SatelliteOfTheDay/SpotlightCard.tsx`:
- Around line 147-151: Update the fallback rendering in SpotlightCard so
liveQuery.isError displays an error-specific message with a retry action, rather
than the catalog re-entry warning. Only show the existing “may have re-entered”
message when the query completed successfully without orbital data, and keep the
loading state excluded.

In `@frontend/src/constants/spotlightSatellites.ts`:
- Around line 87-96: Update the NOAA-19 entry in the satellite catalog to use
status "Retired" and revise its mission_summary and facts text to describe its
forecasting contribution in historical, past-tense wording rather than implying
current operation.

In `@frontend/src/hooks/useSatelliteOfTheDay.ts`:
- Around line 31-36: Update the selection state around dailyIndex and
manualIndex so the date key is stored in state and refreshed at the next
midnight, rather than relying on useMemo with empty dependencies. Recompute
dailyIndex when that date key changes, and clear manualIndex in the same
date-change flow so previews do not persist across midnight.
🪄 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: 52e1b838-e340-432c-b597-7f4116479f52

📥 Commits

Reviewing files that changed from the base of the PR and between 9152665 and 351beab.

📒 Files selected for processing (6)
  • frontend/src/components/EarthTwin.tsx
  • frontend/src/components/SatelliteOfTheDay/SpotlightCard.tsx
  • frontend/src/components/SatelliteSpotlight/SpotlightManager.tsx
  • frontend/src/constants/spotlightSatellites.ts
  • frontend/src/hooks/useSatelliteOfTheDay.ts
  • frontend/src/pages/Dashboard.tsx

Comment on lines +351 to +365
const flyToSatellite = useCallback((catalogNumber: string) => {
const viewer = viewerRef.current;
const obj = catalogMapRef.current.get(catalogNumber);
if (!viewer || viewer.isDestroyed() || !obj) return;

const pos = keplerToLatLonAlt(obj);
if (pos) {
const destination = Cesium.Cartesian3.fromDegrees(pos.lon, pos.lat, pos.alt * 1000 + 2000000);
viewer.camera.flyTo({ destination, duration: prefersReducedMotion() ? 0 : 1.5 });
}

// SpotlightManager owns actual selection state; this just tells it
// which satellite to lock onto and open the info panel for.
setFocusRequest({ catalogNumber, nonce: Date.now() });
}, []);

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 | 🟠 Major | ⚡ Quick win

Queue focus requests until the globe catalog is ready.

flyToSatellite returns before setFocusRequest when catalogMapRef has not populated. Since populateEntities() is asynchronous, the card can enable first and the user’s click becomes a permanent no-op. Persist a pending catalog number, then perform the camera flight and set focusRequest after the viewer and catalog entry are available.

🤖 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 `@frontend/src/components/EarthTwin.tsx` around lines 351 - 365, Update
flyToSatellite to persist the requested catalog number when the viewer or
catalog entry is unavailable, rather than returning permanently. After
populateEntities makes the viewer and catalog entry available, consume the
pending request and perform the camera flight and setFocusRequest; preserve the
existing reduced-motion behavior and avoid losing clicks made before catalog
readiness.

Comment on lines +147 to +151
{!canViewOnGlobe && !liveQuery.isLoading && (
<p className="text-[10px] text-status-warning mt-2">
Live orbital data isn't currently available for this object, it may have re-entered or dropped out of the tracked catalog.
</p>
)}

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

Do not report API failures as a re-entry.

This branch treats liveQuery.isError exactly like a successful lookup with missing orbital data. A backend outage therefore tells users the object “may have re-entered.” Render an error-specific message and retry action for isError; reserve this catalog-missing fallback for successful incomplete responses.

🤖 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 `@frontend/src/components/SatelliteOfTheDay/SpotlightCard.tsx` around lines 147
- 151, Update the fallback rendering in SpotlightCard so liveQuery.isError
displays an error-specific message with a retry action, rather than the catalog
re-entry warning. Only show the existing “may have re-entered” message when the
query completed successfully without orbital data, and keep the loading state
excluded.

Comment on lines +87 to +96
catalog_number: '33591',
name: 'NOAA-19',
operator: 'NOAA',
launch_date: '2009-02-06',
status: 'Active',
mission_summary:
'The final satellite in the POES series, providing global weather imagery and atmospheric soundings for forecasting.',
facts: [
'Data from NOAA polar orbiters feeds directly into daily weather forecasts and hurricane tracking.',
],

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

Mark NOAA-19 as retired.

NOAA-19 was decommissioned on August 13, 2025; showing ACTIVE and present-tense operational forecasting claims is incorrect. Use Retired and adjust the summary/fact to historical wording. (ospo.noaa.gov)

Proposed fix
-    status: 'Active',
+    status: 'Retired',
     mission_summary:
-      'The final satellite in the POES series, providing global weather imagery and atmospheric soundings for forecasting.',
+      'The final satellite in the POES series, which provided global weather imagery and atmospheric soundings for forecasting.',
📝 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
catalog_number: '33591',
name: 'NOAA-19',
operator: 'NOAA',
launch_date: '2009-02-06',
status: 'Active',
mission_summary:
'The final satellite in the POES series, providing global weather imagery and atmospheric soundings for forecasting.',
facts: [
'Data from NOAA polar orbiters feeds directly into daily weather forecasts and hurricane tracking.',
],
catalog_number: '33591',
name: 'NOAA-19',
operator: 'NOAA',
launch_date: '2009-02-06',
status: 'Retired',
mission_summary:
'The final satellite in the POES series, which provided global weather imagery and atmospheric soundings for forecasting.',
facts: [
'Data from NOAA polar orbiters feeds directly into daily weather forecasts and hurricane tracking.',
],
🤖 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 `@frontend/src/constants/spotlightSatellites.ts` around lines 87 - 96, Update
the NOAA-19 entry in the satellite catalog to use status "Retired" and revise
its mission_summary and facts text to describe its forecasting contribution in
historical, past-tense wording rather than implying current operation.

Comment on lines +31 to +36
const dailyIndex = useMemo(() => {
if (SPOTLIGHT_SATELLITES.length === 0) return 0;
return hashString(todayKey()) % SPOTLIGHT_SATELLITES.length;
}, []);

const [manualIndex, setManualIndex] = useState<number | null>(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 | 🟠 Major | ⚡ Quick win

Refresh the selection at the day boundary.

useMemo(..., []) freezes dailyIndex for the component lifetime, and a manual preview also survives midnight. Keep a date key in state, schedule its update for the next midnight, and clear manualIndex when it changes so the 24-hour rotation actually refreshes.

🤖 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 `@frontend/src/hooks/useSatelliteOfTheDay.ts` around lines 31 - 36, Update the
selection state around dailyIndex and manualIndex so the date key is stored in
state and refreshed at the next midnight, rather than relying on useMemo with
empty dependencies. Recompute dailyIndex when that date key changes, and clear
manualIndex in the same date-change flow so previews do not persist across
midnight.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI Artificial Intelligence and Machine Learning bug Something isn't working documentation Improvements or additions to documentation ECSoC26-L1 Level 1 contribution for the ECSoC26 event. ECSoC26 Official label for ECSoC26 event contributions. enhancement New feature or request frontend Frontend development size/L Large contribution requiring significant work. size:L Large size type:bug Fixes an existing bug or unexpected behavior. type:documentation Improves project documentation. type:feature Introduces a new feature or enhancement. type:frontend Changes frontend or client-side code.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🛰️ "Satellite of the Day" Spotlight

1 participant