Skip to content
Open
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
58 changes: 36 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,36 @@
# 🚀 Project: Complex NASA API

### Goal: Use NASA's API to return all of their facility locations (~400). Display the name of the facility, its location, and the weather at the facility currently.

### How to submit your code for review:

- Fork and clone this repo
- Create a new branch called answer
- Checkout answer branch
- Push to your fork
- Issue a pull request
- Your pull request description should contain the following:
- (1 to 5 no 3) I completed the challenge
- (1 to 5 no 3) I feel good about my code
- Anything specific on which you want feedback!

Example:
```
I completed the challenge: 5
I feel good about my code: 4
I'm not sure if my constructors are setup cleanly...
```
# 🌌 NASA Facilities + Current Weather

An interactive display of all NASA facilities across the United States, paired with live current weather data for each location.
Built to explore large-scale public data handling and chained API requests, this project combines clean visuals with real-time information.

[Live Demo](https://complex-nasa-api.vercel.app/)

![screenshot](img/facility.png "NASA Facilities + Current Weather")

---

## How It’s Made
**Tech used:** HTML, CSS, JavaScript

This project fetches NASA facility data through a CORS proxy and dynamically displays each location as a card with its name, city, and state.
Each card includes a “Show weather” button that triggers a second fetch from the Open-Meteo API to display live conditions for that facility.
The interface is styled with a dark cosmic theme inspired by NASA imagery and designed for easy readability across hundreds of results.

---

## Optimizations
- Add filters by state or region.
- Introduce a search bar for faster navigation.
- Include a “Hide weather” toggle.
- Experiment with data caching or lazy loading.
- Optionally visualize facility locations on a map.

---

## Lessons Learned
- Working with nested fetch requests between two public APIs.
- Efficiently rendering large datasets with DOM manipulation.
- Managing hidden elements that reveal on user interaction.
- Maintaining clean, readable UI even with heavy data flow.
- Simplifying chained API logic without complex frameworks.

102 changes: 102 additions & 0 deletions css/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
* {
box-sizing: border-box;
}

html,
body {
margin: 0;
padding: 0;
min-height: 100vh;
font-family: "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
color: #e8f1ff;
background: url("https://www.nasa.gov/wp-content/uploads/2023/07/hs-2015-02-a-large_web.jpg") center/cover no-repeat fixed;
}

body::before {
content: "";
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.65);
z-index: -1;
}

h1 {
text-align: center;
margin: 28px auto 6px;
font-size: 32px;
font-weight: 800;
color: #a6e9ff;
letter-spacing: 0.5px;
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
}

p {
text-align: center;
max-width: 800px;
margin: 8px auto;
color: #d2e5ff;
line-height: 1.4;
}

#facilityList {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 20px;
padding: 24px;
width: 90%;
max-width: 1200px;
margin: 0 auto 60px;
}

#facilityList li {
list-style: none;
background: rgba(20, 28, 52, 0.85);
border: 1px solid rgba(80, 100, 140, 0.4);
border-radius: 12px;
padding: 18px;
color: #eaf4ff;
display: flex;
flex-direction: column;
gap: 10px;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}

#facilityList li:hover {
transform: translateY(-2px);
box-shadow: 0 0 14px rgba(164, 200, 255, 0.25);
}

button {
align-self: flex-start;
padding: 8px 14px;
background-color: #3b82f6;
color: #fff;
border: none;
border-radius: 8px;
font-size: 15px;
cursor: pointer;
transition: background-color 0.2s ease, transform 0.2s ease;
}

button:hover {
background-color: #2563eb;
transform: scale(1.03);
}


.weather {
display: none;
background: rgba(35, 45, 75, 0.85);
border: 1px solid #506a9b;
border-radius: 8px;
padding: 10px;
font-size: 15px;
color: #eaf4ff;
line-height: 1.4;
}


#statusMsg {
margin-top: 10px;
color: #bcd6ff;
}
Binary file added img/facility.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/stars.avif
Binary file not shown.
14 changes: 14 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<h1>NASA Facilities</h1>
<p id="statusMsg">Loading…</p>
<section id="facilityList"></section>
<script src="js/main.js" defer></script>
</body>
</html>
80 changes: 80 additions & 0 deletions js/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
const statusMsg = document.querySelector("#statusMsg");
const list = document.querySelector("#facilityList");

// keep proxy link
const nasaURL = "https://corsproxy.io/?url=https://data.nasa.gov/docs/legacy/gvk9-iz74.json";

fetch(nasaURL)
.then(res => res.json())
.then(data => {
const facilities = data;

if (!facilities || !facilities.length) {
statusMsg.textContent = "No facilities found.";
return;
}

statusMsg.textContent = "NASA facilities loaded. Click 'Show weather' to view conditions.";

facilities.forEach(facility => {
const name = facility.facility || "Unknown Facility";
const center = facility.center || "Unknown Center";
const city = facility.city || "";
const state = facility.state || "";
const lat = facility.location && facility.location.latitude;
const lon = facility.location && facility.location.longitude;

// create card
const li = document.createElement("li");

const title = document.createElement("div");
title.textContent = `${center} — ${name} (${city}, ${state})`;
li.appendChild(title);

// weather button
const btn = document.createElement("button");
btn.textContent = "Show weather";

// weather box
const weather = document.createElement("div");
weather.className = "weather";
weather.style.display = "none"; // start hidden

// show weather when clicked
btn.addEventListener("click", () => {
weather.style.display = "block"; // make it visible

if (!lat || !lon) {
weather.textContent = "No coordinates available.";
return;
}

weather.textContent = "Loading weather...";

const weatherUrl = `https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}&current_weather=true`;

fetch(weatherUrl)
.then(res => res.json())
.then(data => {
const current = data.current_weather;
if (current) {
weather.textContent = `Temp: ${current.temperature}°C | Wind: ${current.windspeed} m/s`;
} else {
weather.textContent = "No weather data found.";
}
})
.catch(err => {
console.log("Error:", err);
weather.textContent = "Error fetching weather.";
});
});

li.appendChild(btn);
li.appendChild(weather);
list.appendChild(li);
});
})
.catch(err => {
console.log("Error:", err);
statusMsg.textContent = "Error loading NASA data. Please reload.";
});