Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export default function App() {
<Route path="/" element={<Home />} />
<Route path="/weather" element={<Weather />} />
<Route path="/crypto" element={<Crypto />} />
<Route path="/space" element={<Space />} />
<Route path="/space" element={<Space theme={theme} />} />
<Route path="/movies" element={<Movies />} />
<Route path="/recipes" element={<Recipes />} />
<Route path="/trivia" element={<Trivia />} />
Expand Down
204 changes: 175 additions & 29 deletions src/pages/Space.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
* ---------------------------------
* Easy:
* - [x] Refresh button / auto-refresh interval selector
* - [ ] Show last updated timestamp
* - [ ] Style astronauts list with craft grouping
* - [x] Show last updated timestamp
* - [x] Style astronauts list with craft grouping
* - [ ] Add loading skeleton or placeholder map area
* Medium:
* - [ ] Integrate Leaflet map w/ marker at ISS coords
Expand All @@ -24,13 +24,16 @@ import Card from '../components/Card.jsx';
import IssMap from '../components/IssMap.jsx';
import DashboardControls from "../components/DashboardControls.jsx";

export default function Space() {

export default function Space({ theme = 'light' }) {
const [iss, setIss] = useState(null);
const [crew, setCrew] = useState([]);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(false);
const [lastUpdated, setLastUpdated] = useState(null);


const isDark = theme === 'dark';

// Fetch both ISS position + crew
async function fetchData() {
try {
Expand All @@ -56,34 +59,177 @@ export default function Space() {
useEffect(() => {
fetchData();
}, []);

//leaflet map component

// Proper dark/light theme colors
const bgColor = isDark ? '#0f172a' : '#f8fafc';
const textColor = isDark ? '#f1f5f9' : '#1e293b';
const cardBg = isDark ? '#1e293b' : '#ffffff';
const subText = isDark ? '#94a3b8' : '#64748b';
const accent = isDark ? '#38bdf8' : '#2563eb';
const listBg = isDark ? '#334155' : '#f1f5f9';
const borderColor = isDark ? '#334155' : '#e2e8f0';

return (
<div>
<h2>Space & Astronomy</h2>
<DashboardControls onRefresh={fetchData} />
<div
style={{
maxWidth: '1000px',
margin: '2rem auto',
padding: '2rem',
backgroundColor: bgColor,
borderRadius: '16px',
boxShadow: isDark
? '0 4px 10px rgba(0,0,0,0.3)'
: '0 4px 10px rgba(0,0,0,0.08)',
color: textColor,
fontFamily: 'Inter, system-ui, sans-serif',
transition: 'background-color 0.3s ease, color 0.3s ease',
border: isDark ? '1px solid #334155' : 'none',
}}
>
<h2
style={{
textAlign: 'center',
marginBottom: '1.8rem',
fontSize: '2rem',
fontWeight: '600',
color: accent,
letterSpacing: '0.5px',
}}
>
🌌 Space & Astronomy Dashboard
</h2>

<div style={{ display: 'flex', justifyContent: 'center', marginBottom: '1.5rem' }}>
<DashboardControls onRefresh={fetchData} />
</div>

{loading && <Loading />}
<ErrorMessage error={error} />
{iss && (
<Card title="ISS Current Location">
<p>Latitude: {iss.iss_position.latitude}</p>
<p>Longitude: {iss.iss_position.longitude}</p>
{lastUpdated && (
<p style={{ fontSize: '0.8rem', color: '#666' }}>
Last updated: {lastUpdated.toLocaleTimeString()}
</p>
)}
<IssMap latitude={iss.iss_position.latitude} longitude={iss.iss_position.longitude} />

<div
style={{
display: 'flex',
flexDirection: 'column',
gap: '1.5rem',
justifyContent: 'center',
}}
>
{iss && (
<Card
title="🛰️ ISS Current Location"
style={{
backgroundColor: cardBg,
borderRadius: '12px',
padding: '1.5rem',
boxShadow: isDark
? '0 2px 8px rgba(0,0,0,0.2)'
: '0 2px 8px rgba(0,0,0,0.05)',
transition: 'all 0.3s ease',
border: `1px solid ${borderColor}`,
}}
>
<div style={{ lineHeight: '1.6' }}>
<p style={{ color: textColor, margin: '0.5rem 0' }}>
<strong>Latitude:</strong> {iss.iss_position.latitude}
</p>
<p style={{ color: textColor, margin: '0.5rem 0' }}>
<strong>Longitude:</strong> {iss.iss_position.longitude}
</p>
{lastUpdated && (
<p style={{ fontSize: '0.9rem', color: subText, margin: '0.5rem 0' }}>
Last updated: {lastUpdated.toLocaleTimeString()}
</p>
)}
</div>

<div style={{ marginTop: '1rem' }}>
<IssMap
latitude={iss.iss_position.latitude}
longitude={iss.iss_position.longitude}
/>
</div>
</Card>
)}

<Card
title={`👩‍🚀 Astronauts in Space (${crew.length})`}
style={{
backgroundColor: cardBg,
borderRadius: '12px',
padding: '1.5rem',
boxShadow: isDark
? '0 2px 8px rgba(0,0,0,0.2)'
: '0 2px 8px rgba(0,0,0,0.05)',
transition: 'all 0.3s ease',
border: `1px solid ${borderColor}`,
}}
>
<ul style={{
paddingLeft: '0',
listStyleType: 'none',
margin: '0',
display: 'flex',
flexDirection: 'column',
gap: '0.5rem'
}}>
{crew.map((p) => (
<li
key={p.name}
style={{
backgroundColor: listBg,
padding: '0.75rem 1rem',
borderRadius: '8px',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
transition: 'all 0.3s ease',
border: `1px solid ${borderColor}`,
}}
>
<span style={{
fontWeight: '500',
color: textColor
}}>
{p.name}
</span>
<span style={{
fontSize: '0.9rem',
color: subText,
backgroundColor: isDark ? '#475569' : '#e2e8f0',
padding: '0.25rem 0.75rem',
borderRadius: '6px',
fontWeight: '500'
}}>
🚀 {p.craft}
</span>
</li>
))}
</ul>
</Card>
)}
<Card title={`Astronauts in Space (${crew.length})`}>
<ul>
{crew.map(p => (
<li key={p.name}>{p.name} — {p.craft}</li>
))}
</ul>
</Card>
{/* TODO: Add next ISS pass prediction form */}
</div>

<p
style={{
marginTop: '2rem',
textAlign: 'center',
fontSize: '0.9rem',
color: subText,
}}
>
Data sourced from{' '}
<a
href="http://api.open-notify.org"
style={{
color: accent,
textDecoration: 'none',
fontWeight: '500',
}}
onMouseEnter={(e) => e.target.style.textDecoration = 'underline'}
onMouseLeave={(e) => e.target.style.textDecoration = 'none'}
>
Open Notify API
</a>
</p>
</div>
);
}
}
135 changes: 35 additions & 100 deletions src/pages/Weather.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -411,39 +411,41 @@ export default function Weather() {
Switch to °{unit === "C" ? "F" : "C"}
</button>
</div>
{loading && <Loading />}
{error && (
<ErrorMessage
message={error.message}
onRetry={() => fetchWeather(city)}
/>
)}

{data && !loading && (
<div className="dashboard-grid">
{/* Current Weather */}
<Card title="Current Weather" size="large">
<h2>{data.nearest_area?.[0]?.areaName?.[0]?.value || city}</h2>
<p style={{ display: "flex", alignItems: "center", gap: "12px" }}>
{current && getIconUrl(current.weatherIconUrl) && (
<img
src={getIconUrl(current.weatherIconUrl)}
alt={current.weatherDesc?.[0]?.value || "weather icon"}
style={{ width: 48, height: 48, objectFit: "contain" }}
/>
)}
<span>
<strong>Temperature:</strong>{" "}
{displayTemp(Number(current.temp_C))}°{unit}
</span>
</p>
<p>
<strong>Humidity:</strong> {current.humidity}%
</p>
<p>
<strong>Desc:</strong> {current.weatherDesc?.[0]?.value}
</p>
</Card>
</div>

{loading && <Loading />}
{error && (
<ErrorMessage
message={error.message}
onRetry={() => fetchWeather(city)}
/>
)}

{data && !loading && (
<div className="dashboard-grid">
{/* Current Weather */}
<Card title="Current Weather" size="large">
<h2>{data.nearest_area?.[0]?.areaName?.[0]?.value || city}</h2>
<p style={{ display: "flex", alignItems: "center", gap: "12px" }}>
{current && getIconUrl(current.weatherIconUrl) && (
<img
src={getIconUrl(current.weatherIconUrl)}
alt={current.weatherDesc?.[0]?.value || "weather icon"}
style={{ width: 48, height: 48, objectFit: "contain" }}
/>
)}
<span>
<strong>Temperature:</strong>{" "}
{displayTemp(Number(current.temp_C))}°{unit}
</span>
</p>
<p>
<strong>Humidity:</strong> {current.humidity}%
</p>
<p>
<strong>Desc:</strong> {current.weatherDesc?.[0]?.value}
</p>
</Card>

{/* 3-Day Forecast */}
{forecast.map((day, i) => {
Expand Down Expand Up @@ -570,73 +572,6 @@ export default function Weather() {
);
})}
</div>

{loading && <Loading />}
{error && (
<ErrorMessage
message={error.message}
onRetry={() => fetchWeather(city)}
/>
)}

{data && !loading && (
<div
className="dashboard-grid"
style={{ position: "relative", zIndex: 10 }}
>
{/* Current Weather */}
<Card title="Current Weather" size="large">
<h2>{data.nearest_area?.[0]?.areaName?.[0]?.value || city}</h2>
<p>
<strong>Temperature:</strong>{" "}
{displayTemp(Number(current.temp_C))}°{unit}
</p>
<p>
<strong>Humidity:</strong> {current.humidity}%
</p>
<p>
<strong>Desc:</strong> {current.weatherDesc?.[0]?.value}
</p>
</Card>

{/* 3-Day Forecast */}
{forecast.map((day, i) => {
const condition =
day.hourly?.[0]?.weatherDesc?.[0]?.value || "Clear";
const badge = getBadgeStyle(condition);

return (
<Card key={i} title={i === 0 ? "Today" : `Day ${i + 1}`}>
{/* Badge Section */}
<div
style={{
backgroundColor: badge.color,
borderRadius: "8px",
padding: "4px 8px",
display: "inline-block",
fontSize: "12px",
fontWeight: "bold",
marginBottom: "8px",
color: "#333",
}}
>
{badge.label}
</div>

<p>
<strong>Avg Temp:</strong> {displayTemp(Number(day.avgtempC))}
°{unit}
</p>
<p>
<strong>Sunrise:</strong> {day.astronomy?.[0]?.sunrise}
</p>
<p>
<strong>Sunset:</strong> {day.astronomy?.[0]?.sunset}
</p>
</Card>
);
})}
</div>
)}
</div>
</div>
Expand Down
Loading