feat: implement live mission control event timeline (#172) - #183
Conversation
🤖 CodeAnt AI — Review Status
|
Thanks for using CodeAnt! 🎉We're free for open-source projects. if you're enjoying it, help us grow by sharing. Share on X · |
📝 WalkthroughWalkthroughThe PR adds a mission-events API and a frontend event timeline. The timeline supports live event generation, filtering, search, severity handling, incident simulation, acknowledgments, expandable details, and dashboard navigation. ChangesMission event timeline
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant EventTimelinePage
participant useEventTimeline
participant useEventTimelineStore
participant EventTimelineCard
EventTimelinePage->>useEventTimeline: subscribe to timeline state
useEventTimeline->>useEventTimelineStore: start live streaming
useEventTimelineStore-->>useEventTimeline: generate event
useEventTimeline-->>EventTimelinePage: provide filtered events
EventTimelinePage->>EventTimelineCard: render event
EventTimelineCard->>useEventTimeline: acknowledge event
useEventTimeline->>useEventTimelineStore: update event status
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment Warning |
There was a problem hiding this comment.
Actionable comments posted: 13
🧹 Nitpick comments (11)
backend/api/v1/endpoints/events.py (1)
8-14: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winRemove the unused database session and model imports.
The handler never uses
db.Depends(get_db)still checks out a session from the pool on every request and then discards it.List,OrbitalEvent,CollisionPrediction, andSpaceWeatherare also unused.Drop them until the database-backed implementation replaces the static data. Removing the
dbparameter also clears the Ruff B008 hint on line 123.♻️ Proposed cleanup
-from typing import List, Optional +from typing import Optional from datetime import datetime, timezone, timedelta -from fastapi import APIRouter, Query, Depends -from sqlalchemy.orm import Session - -from database.session import get_db -from models.db_models import OrbitalEvent, CollisionPrediction, SpaceWeather +from fastapi import APIRouter, Querylimit: int = Query(50, ge=1, le=200), - db: Session = Depends(get_db) ):Also applies to: 123-123
🤖 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 `@backend/api/v1/endpoints/events.py` around lines 8 - 14, Remove the unused List, OrbitalEvent, CollisionPrediction, and SpaceWeather imports, then update the affected event handler to remove its db parameter and Depends(get_db) dependency. Keep the remaining request parameters and static-data behavior unchanged.Source: Linters/SAST tools
frontend/src/hooks/useEventTimeline.ts (2)
124-126: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRename
unacknowledgedCriticalEvents.The predicate accepts
CRITICALandHIGH, but the name statesCriticalonly. A reader who wires this value into a critical-only alert path getsHIGHevents as well.Use a name that matches the predicate, for example
unacknowledgedPriorityEvents. Update the consumers in the timeline page and the incident banner.🤖 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/useEventTimeline.ts` around lines 124 - 126, Rename unacknowledgedCriticalEvents to a name reflecting both CRITICAL and HIGH severities, such as unacknowledgedPriorityEvents, and update all consumers in the timeline page and incident banner to use the renamed value without changing the predicate.
22-27: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winThe 10-second ticker re-filters the list even when no time filter is active.
currentTimeis a dependency of thefilteredEventsmemo at line 91. Each tick invalidates the memo, so the hook re-filters and re-sorts every event and returns a new array reference. All consumers then re-render.
currentTimeis read only inside thefilters.timeRange !== 'ALL'branch at line 61. The default filter is'ALL', so in the default state this work produces no change to the output.Start the ticker only when a bounded time range is active.
♻️ Proposed refactor
useEffect(() => { + if (filters.timeRange === 'ALL') return; const timer = setInterval(() => setCurrentTime(Date.now()), 10000); return () => clearInterval(timer); - }, []); + }, [filters.timeRange]);🤖 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/useEventTimeline.ts` around lines 22 - 27, Update the timer setup in the useEventTimeline hook so the 10-second interval runs only when filters.timeRange is a bounded range rather than 'ALL'. Stop and clean up any existing timer when the filter returns to 'ALL', while preserving currentTime updates for active time-range filtering.frontend/src/services/api.ts (1)
193-193: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUse a top-level type import.
The inline
import('@/types/events').TimelineEvent[]differs from the import style used by the other methods in this file. A top-levelimport typereads better and keeps the type imports in one place.♻️ Proposed refactor
Add to the existing type imports at the top of the file:
+import type { TimelineEvent } from '`@/types/events`';Then simplify the call:
- return apiFetch<import('`@/types/events`').TimelineEvent[]>(`/events?${q}`); + return apiFetch<TimelineEvent[]>(`/events?${q}`);🤖 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/services/api.ts` at line 193, Replace the inline import type in the `apiFetch` call for the events endpoint with a top-level `import type` alongside the file’s existing type imports, then reference `TimelineEvent[]` directly in that call while preserving the current request behavior.frontend/src/components/timeline/EventTimelineFilterBar.tsx (2)
126-142: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExpose the active category to assistive technology.
The selected pill is indicated only by color classes. Screen reader users cannot determine which category is active. Add
aria-pressed={isActive}to each pill.♻️ Proposed refactor
<button key={cat.value} + type="button" + aria-pressed={isActive} onClick={() => onFilterChange('category', cat.value)}🤖 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/timeline/EventTimelineFilterBar.tsx` around lines 126 - 142, Update the category buttons rendered in CATEGORIES.map within EventTimelineFilterBar to include aria-pressed={isActive}, exposing each pill’s selected state to assistive technology while preserving the existing visual styling and click behavior.
148-198: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd accessible names to the search input, the clear button, and both selects.
The search input relies on
placeholderonly. The clear button contains an icon only. Neitherselecthas a label. Screen reader users cannot identify these four controls.♻️ Proposed refactor
<input type="text" + aria-label="Search events by title, NORAD ID, or satellite" value={filters.searchQuery}<button + type="button" + aria-label="Clear search query" onClick={() => onFilterChange('searchQuery', '')}<select + aria-label="Filter by severity" value={filters.severity}<select + aria-label="Filter by time range" value={filters.timeRange}🤖 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/timeline/EventTimelineFilterBar.tsx` around lines 148 - 198, Add accessible names to the search input, the search-query clear button, and both select controls in the filter bar. Use descriptive aria-labels or associated labels that identify search, clear search, severity, and time range, while preserving the existing filtering behavior.frontend/src/components/timeline/HighPriorityIncidentBanner.tsx (2)
119-136: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueMerge the two identical
CONJUNCTIONconditions.Lines 119 and 129 evaluate the same condition. Wrap both buttons in a single fragment guarded once.
🤖 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/timeline/HighPriorityIncidentBanner.tsx` around lines 119 - 136, In HighPriorityIncidentBanner, merge the two adjacent currentIncident.category === 'CONJUNCTION' guards into one shared conditional wrapper containing both the PLAN MANEUVER and RISK CENTER buttons, preserving each button’s navigation and styling.
18-36: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueMove the empty check inside
AnimatePresenceso the exit animation runs.The component returns
nullat line 18 before renderingAnimatePresence. When the operator acknowledges the last incident, React unmountsAnimatePresenceand its child together. Theexittransition at line 29 never plays for that case. KeepAnimatePresencemounted and render the child conditionally.♻️ Proposed refactor
- if (incidents.length === 0) return null; - - const currentIncident = incidents[0]; // Show top urgent incident - const isCritical = currentIncident.severity === 'CRITICAL'; + const currentIncident = incidents[0]; // Show top urgent incident + const isCritical = currentIncident?.severity === 'CRITICAL'; return ( <AnimatePresence mode="wait"> - <motion.div - key={currentIncident.id} + {currentIncident && ( + <motion.div + key={currentIncident.id}Close the conditional after the
motion.divclosing tag at line 151.🤖 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/timeline/HighPriorityIncidentBanner.tsx` around lines 18 - 36, Keep AnimatePresence mounted in HighPriorityIncidentBanner and move the incidents.length === 0 condition inside it, rendering the motion.div only when an incident exists. Preserve the currentIncident and severity logic for non-empty incidents so acknowledging the final incident allows its exit animation to run.frontend/src/pages/EventTimeline.tsx (2)
111-134: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueKeep
AnimatePresencemounted across the empty-state switch.
AnimatePresencesits inside theelsebranch. When the last event is filtered out, React unmountsAnimatePresencetogether with its children, so the card exit animations do not play. MoveAnimatePresenceoutside the conditional and render both branches inside it.🤖 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/pages/EventTimeline.tsx` around lines 111 - 134, Move AnimatePresence outside the events.length conditional in the timeline render, keeping it mounted while switching between the empty state and event cards. Render both branches as its children so EventTimelineCard instances can complete their exit animations when filters remove the last event.
24-33: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winIsolate the 1-second clock to avoid re-rendering the whole event list.
setUtcClockruns every second and re-rendersEventTimelinePage. That re-rendersHighPriorityIncidentBanner,EventTimelineFilterBar, and everyEventTimelineCardin the list.EventTimelineCardis not memoized and animates withmotion.div, so the cost grows with the number of events.Move the clock into a small child component, or memoize the card list.
♻️ Proposed refactor: extract the clock component
+const UtcClock: React.FC = () => { + const [utcClock, setUtcClock] = useState<string>(''); + + useEffect(() => { + const updateClock = () => { + setUtcClock(new Date().toUTCString().replace('GMT', 'UTC')); + }; + updateClock(); + const interval = setInterval(updateClock, 1000); + return () => clearInterval(interval); + }, []); + + return ( + <p className="text-[10px] font-bold text-primary-container/80 font-technical-data mt-1 truncate"> + {utcClock.substring(17, 25)} + </p> + ); +};Then render
<UtcClock />in place of the inline paragraph at lines 85-87.🤖 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/pages/EventTimeline.tsx` around lines 24 - 33, Extract the utcClock state and one-second update effect from EventTimelinePage into a small UtcClock child component, then render UtcClock where the inline clock paragraph currently appears. Keep the existing UTC formatting and interval cleanup unchanged so only the clock component re-renders each second, leaving HighPriorityIncidentBanner, EventTimelineFilterBar, and EventTimelineCard rendering unaffected.frontend/src/components/timeline/EventTimelineCard.tsx (1)
64-73: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win
formatRelativeTimedepends on an external re-render to stay current.The function reads
Date.now()during render. The card holds no timer. Relative labels update only becauseEventTimelinePagere-renders every second for its UTC clock. If that clock is isolated into its own component, the labels freeze at their mount value.Derive the relative label from the
currentTimevalue thatuseEventTimelinealready maintains, and pass it as a prop.🤖 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/timeline/EventTimelineCard.tsx` around lines 64 - 73, The formatRelativeTime flow should use the currentTime maintained by useEventTimeline instead of calling Date.now() during render. Pass currentTime through the relevant component props and use it when calculating each card’s relative label, ensuring updates remain independent of EventTimelinePage’s UTC clock re-render.
🤖 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 `@backend/api/v1/endpoints/events.py`:
- Around line 19-22: The backend seed timestamps in
backend/api/v1/endpoints/events.py lines 19-22 must be calculated inside the
request handler rather than at module import, using the current time minus each
event’s offset for every response. In frontend/src/store/eventTimelineStore.ts
lines 10-122, replace the eagerly created INITIAL_EVENTS with a lazy factory
invoked during store creation and re-initialization so seed timestamps refresh
whenever the store is rebuilt.
- Around line 153-156: In the events endpoint response, capture the full match
count before the `results` list is truncated by `limit`, then use that saved
count for the metadata `total` field instead of `len(results)`. Keep the sliced
`results` payload unchanged.
In `@frontend/src/components/timeline/EventTimelineCard.tsx`:
- Around line 315-321: Update the EventTimelineCard “TRACK ON 3D GLOBE” action
to render only when event.norad_id exists, and navigate to the dashboard while
passing that NORAD identifier through route state or a query parameter. Update
the Dashboard flow to consume the identifier and call
earthTwinRef.current?.flyToSatellite with it so the globe focuses the event’s
satellite.
- Around line 222-231: Extract the duplicated miss-distance formatting into a
shared formatMissDistance helper that rounds metre values with toFixed(0) and
kilometre values with toFixed(2). Update the conditional render in
frontend/src/components/timeline/EventTimelineCard.tsx lines 222-231 and
frontend/src/components/timeline/HighPriorityIncidentBanner.tsx lines 89-98 to
use the helper.
- Around line 114-117: Make the expandable header div in EventTimelineCard
keyboard operable without changing it to a button: add role="button",
tabIndex={0}, and aria-expanded={expanded}, and handle Enter and Space key
events by toggling expansion while preventing the Space key’s default behavior.
Preserve the existing click toggle and nested acknowledge button.
- Around line 79-80: Update the severityConfig and categoryConfig lookups in
EventTimelineCard so unknown event.severity or event.category values resolve to
safe default configurations instead of undefined. Reuse existing
severity/category config entries where possible, and preserve the current
classes and props behavior for recognized values.
- Around line 297-307: Filter event.external_references before mapping in
EventTimelineCard, allowing only references whose ref.url uses the http: or
https: protocols. Render anchors only for approved URLs, while preserving the
existing link markup and labels for valid references.
In `@frontend/src/components/timeline/EventTimelineFilterBar.tsx`:
- Around line 56-79: Update the stream-status logic and button rendering around
isLive and onTogglePauseStream to handle streamStatus === 'DISCONNECTED'
separately. Render a disabled/disconnected state with an appropriate label and
icon, prevent the pause/resume action, and preserve the existing LIVE and paused
behavior.
In `@frontend/src/components/timeline/HighPriorityIncidentBanner.tsx`:
- Around line 20-21: Update the incident selection in HighPriorityIncidentBanner
so currentIncident is chosen from incidents by highest severity rather than
fixed to incidents[0]. Preserve the existing banner behavior while ensuring
CRITICAL incidents take precedence over HIGH and other severities regardless of
insertion order.
In `@frontend/src/pages/EventTimeline.tsx`:
- Around line 112-127: Update the empty-state rendering around events.length ===
0 to use rawEventsCount: show a “no events yet” message without filter-reset
guidance when rawEventsCount is zero, and retain the current filtered-empty
message and RESET ALL FILTERS button when raw events exist but the filters
produce no matches.
- Around line 84-88: Remove the hardcoded '18:20:00 UTC' fallback from the time
display in EventTimeline, so the utcClock substring renders as an empty
placeholder when no UTC time is available.
- Around line 76-81: Update the stream status text in EventTimeline to choose
its color based on streamStatus: retain the success color for active/healthy
status, and use the appropriate non-success status color for PAUSED and
DISCONNECTED. Keep the existing status text and styling unchanged aside from the
value-based color mapping.
In `@frontend/src/store/eventTimelineStore.ts`:
- Around line 315-339: Update startLiveStreaming to read the current streamSpeed
inside each tick and scale event generation accordingly, while preserving the
documented 1x, 2x, and 5x behavior. Add module-level interval and
reference-count state near eventCounter so repeated callers share one timer;
increment the count on start, create the interval only when transitioning from
zero callers, and have each cleanup decrement the count and clear/reset the
shared interval only when no callers remain.
---
Nitpick comments:
In `@backend/api/v1/endpoints/events.py`:
- Around line 8-14: Remove the unused List, OrbitalEvent, CollisionPrediction,
and SpaceWeather imports, then update the affected event handler to remove its
db parameter and Depends(get_db) dependency. Keep the remaining request
parameters and static-data behavior unchanged.
In `@frontend/src/components/timeline/EventTimelineCard.tsx`:
- Around line 64-73: The formatRelativeTime flow should use the currentTime
maintained by useEventTimeline instead of calling Date.now() during render. Pass
currentTime through the relevant component props and use it when calculating
each card’s relative label, ensuring updates remain independent of
EventTimelinePage’s UTC clock re-render.
In `@frontend/src/components/timeline/EventTimelineFilterBar.tsx`:
- Around line 126-142: Update the category buttons rendered in CATEGORIES.map
within EventTimelineFilterBar to include aria-pressed={isActive}, exposing each
pill’s selected state to assistive technology while preserving the existing
visual styling and click behavior.
- Around line 148-198: Add accessible names to the search input, the
search-query clear button, and both select controls in the filter bar. Use
descriptive aria-labels or associated labels that identify search, clear search,
severity, and time range, while preserving the existing filtering behavior.
In `@frontend/src/components/timeline/HighPriorityIncidentBanner.tsx`:
- Around line 119-136: In HighPriorityIncidentBanner, merge the two adjacent
currentIncident.category === 'CONJUNCTION' guards into one shared conditional
wrapper containing both the PLAN MANEUVER and RISK CENTER buttons, preserving
each button’s navigation and styling.
- Around line 18-36: Keep AnimatePresence mounted in HighPriorityIncidentBanner
and move the incidents.length === 0 condition inside it, rendering the
motion.div only when an incident exists. Preserve the currentIncident and
severity logic for non-empty incidents so acknowledging the final incident
allows its exit animation to run.
In `@frontend/src/hooks/useEventTimeline.ts`:
- Around line 124-126: Rename unacknowledgedCriticalEvents to a name reflecting
both CRITICAL and HIGH severities, such as unacknowledgedPriorityEvents, and
update all consumers in the timeline page and incident banner to use the renamed
value without changing the predicate.
- Around line 22-27: Update the timer setup in the useEventTimeline hook so the
10-second interval runs only when filters.timeRange is a bounded range rather
than 'ALL'. Stop and clean up any existing timer when the filter returns to
'ALL', while preserving currentTime updates for active time-range filtering.
In `@frontend/src/pages/EventTimeline.tsx`:
- Around line 111-134: Move AnimatePresence outside the events.length
conditional in the timeline render, keeping it mounted while switching between
the empty state and event cards. Render both branches as its children so
EventTimelineCard instances can complete their exit animations when filters
remove the last event.
- Around line 24-33: Extract the utcClock state and one-second update effect
from EventTimelinePage into a small UtcClock child component, then render
UtcClock where the inline clock paragraph currently appears. Keep the existing
UTC formatting and interval cleanup unchanged so only the clock component
re-renders each second, leaving HighPriorityIncidentBanner,
EventTimelineFilterBar, and EventTimelineCard rendering unaffected.
In `@frontend/src/services/api.ts`:
- Line 193: Replace the inline import type in the `apiFetch` call for the events
endpoint with a top-level `import type` alongside the file’s existing type
imports, then reference `TimelineEvent[]` directly in that call while preserving
the current request behavior.
🪄 Autofix
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: 1fec64c0-e036-4140-b53f-16cb7a254f17
⛔ Files ignored due to path filters (14)
backend/__pycache__/debug_log.cpython-314.pycis excluded by!**/*.pycbackend/api/__pycache__/__init__.cpython-314.pycis excluded by!**/*.pycbackend/api/__pycache__/router.cpython-314.pycis excluded by!**/*.pycbackend/api/v1/__pycache__/__init__.cpython-314.pycis excluded by!**/*.pycbackend/api/v1/endpoints/__pycache__/__init__.cpython-314.pycis excluded by!**/*.pycbackend/api/v1/endpoints/__pycache__/auth.cpython-314.pycis excluded by!**/*.pycbackend/app/__pycache__/__init__.cpython-314.pycis excluded by!**/*.pycbackend/app/__pycache__/main.cpython-314.pycis excluded by!**/*.pycbackend/app/core/__pycache__/__init__.cpython-314.pycis excluded by!**/*.pycbackend/app/core/__pycache__/config.cpython-314.pycis excluded by!**/*.pycbackend/app/core/__pycache__/error_handlers.cpython-314.pycis excluded by!**/*.pycbackend/app/core/__pycache__/exceptions.cpython-314.pycis excluded by!**/*.pycbackend/schemas/__pycache__/__init__.cpython-314.pycis excluded by!**/*.pycbackend/schemas/__pycache__/api_schemas.cpython-314.pycis excluded by!**/*.pyc
📒 Files selected for processing (14)
backend/api/router.pybackend/api/v1/endpoints/events.pyfrontend/src/App.tsxfrontend/src/components/layouts/MainLayout.tsxfrontend/src/components/timeline/EventTimelineCard.tsxfrontend/src/components/timeline/EventTimelineFilterBar.tsxfrontend/src/components/timeline/HighPriorityIncidentBanner.tsxfrontend/src/hooks/useEventTimeline.tsfrontend/src/pages/Dashboard.tsxfrontend/src/pages/EventTimeline.tsxfrontend/src/services/api.tsfrontend/src/store/eventTimelineStore.tsfrontend/src/types/events.tsfrontend/tsconfig.app.json
User description
Summary
This PR implements the Live Mission Control Event Timeline feature (#172), providing satellite operators with a real-time, continuously streaming operational event feed across critical orbital activities.
Key Accomplishments & Technical Details:
Real-Time Event Streaming & Engine:
eventTimelineStore.ts(Zustand) and custom hookuseEventTimeline.tswith a built-in live realistic event generator.SIMULATE INCIDENTemergency alert trigger).logbookStore.ts).Multi-Dimensional Navigation, Filtering & Search:
LAUNCH,CONJUNCTION,MANEUVER,DEBRIS,SPACE_WEATHER, andSYSTEM.CRITICAL(red glow),HIGH(orange glow),MEDIUM(amber), andLOW(cyan).LAST 1 HOUR,LAST 24 HOURS,LAST 7 DAYS, andALL TIME.NEWEST FIRSTandOLDEST FIRST.High-Priority Incident Alert Banner & Expandable Cards:
HighPriorityIncidentBanner.tsx) highlighting active unacknowledged critical alerts with direct action triggers ("ACKNOWLEDGE", "PLAN MANEUVER", "RISK CENTER").EventTimelineCard.tsx) with Framer Motion animations, exact UTC timestamps (YYYY-MM-DD HH:mm:ss UTC), relative time badges (T-minus/elapsed), telemetry metrics grids (miss distance, probability, altitude, velocity, Kp index, delta-V), external reference links (Space-Track, CelesTrak, NOAA SWPC), and quick action buttons ("Track on 3D Globe", "Plan Maneuver", "Space Weather", "Log to Logbook").Integration & Routing:
/dashboard/timelineroute inApp.tsx.MainLayout.tsx.Backend FastAPI Endpoint:
/api/v1/eventsendpoint inbackend/api/v1/endpoints/events.pywith multi-field filtering support (category, severity, search, limit) and registered it inbackend/api/router.py.Related Issue
Fixes #172
Type of Change
Screenshots / Screen Recordings
(Real-time streaming, status metrics, category filter pills, search input bar, high-priority emergency banner, and expandable telemetry cards in action.)
Testing Performed
npm run lintchecked)npx tsc --noEmitverified with 0 errors)Breaking Changes
None. All new routes, components, and backend endpoints are additive and preserve backwards compatibility with existing dashboard modules.
Checklist
ECSoC26 Submission
ECSoC26-L1– BeginnerECSoC26-L2– IntermediateECSoC26-L3– AdvancedCodeAnt-AI Description
Add a live mission event timeline for monitoring orbital activity
What Changed
Impact
✅ Faster access to active orbital alerts✅ Clearer conjunction and space-weather response actions✅ Searchable mission activity history💡 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:
This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.
Example
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:
This helps CodeAnt AI learn and adapt to your team's coding style and standards.
Example
Retrigger review
Ask CodeAnt AI to review the PR again, by typing:
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