diff --git a/index.html b/index.html
index e0437a9..d3c9517 100644
--- a/index.html
+++ b/index.html
@@ -3,21 +3,27 @@
- TV Episodes — Level 400
+ TV Show Project | Level 500
+
-
+
+
diff --git a/script.js b/script.js
index 81f1a88..00e2856 100644
--- a/script.js
+++ b/script.js
@@ -1,41 +1,95 @@
-const episodeSelector = document.getElementById("episodeSelector");
+const showsView = document.getElementById("showsView");
+const episodesView = document.getElementById("episodesView");
+const showSearch = document.getElementById("showSearch");
const episodeSearch = document.getElementById("episodeSearch");
-const episodeCount = document.getElementById("episodeCount");
const showSelector = document.getElementById("showSelector");
-const episodesGrid = document.getElementById("episodesGrid");
+const episodeSelector = document.getElementById("episodeSelector");
+const showCount = document.getElementById("showCount");
+const episodeCount = document.getElementById("episodeCount");
+const backBtn = document.getElementById("backBtn");
-let episodes = [];
-let allShows = [];
+let shows = [];
+let episodesCache = {};
+let currentEpisodes = [];
async function loadShows() {
const res = await fetch("https://api.tvmaze.com/shows");
- allShows = await res.json();
- allShows.sort((a, b) => a.name.localeCompare(b.name));
-
- allShows.forEach((show) => {
+ shows = await res.json();
+ shows.sort((a, b) => a.name.localeCompare(b.name));
+ showSelector.innerHTML = "";
+ shows.forEach((s) => {
const op = document.createElement("option");
- op.value = show.id;
- op.textContent = show.name;
+ op.value = s.id;
+ op.textContent = s.name;
showSelector.appendChild(op);
});
+ renderShowList(shows);
+ updateShowCount(shows.length);
+}
+
+function renderShowList(list) {
+ showsView.innerHTML = "";
+ list.forEach((s) => {
+ const card = document.createElement("div");
+ card.className = "showCard";
+ card.dataset.id = s.id;
+ const genres = s.genres.join(" | ");
+ card.innerHTML = `
+
+
+
${s.name}
+
${s.summary || ""}
+
+
+ Rated: ${s.rating.average || "N/A"}
+ Genres: ${genres}
+ Status: ${s.status}
+ Runtime: ${s.runtime || "-"}
+
+ `;
+ card.addEventListener("click", () => openShow(s.id));
+ showsView.appendChild(card);
+ });
+}
- loadEpisodes(allShows[0].id);
+function updateShowCount(n) {
+ showCount.textContent = `found ${n} shows`;
}
-async function loadEpisodes(showID) {
- const res = await fetch(`https://api.tvmaze.com/shows/${showID}/episodes`);
- episodes = await res.json();
+function searchShows() {
+ const t = showSearch.value.toLowerCase();
+ const filtered = shows.filter(
+ (s) =>
+ s.name.toLowerCase().includes(t) ||
+ s.summary.toLowerCase().includes(t) ||
+ s.genres.join(" ").toLowerCase().includes(t)
+ );
+ renderShowList(filtered);
+ updateShowCount(filtered.length);
+}
+
+async function openShow(id) {
+ showsView.classList.add("hidden");
+ backBtn.classList.remove("hidden");
+ episodeSelector.classList.remove("hidden");
+ episodeSearch.classList.remove("hidden");
+ episodeCount.classList.remove("hidden");
+
+ if (!episodesCache[id]) {
+ const res = await fetch(`https://api.tvmaze.com/shows/${id}/episodes`);
+ episodesCache[id] = await res.json();
+ }
+
+ currentEpisodes = episodesCache[id];
buildEpisodeSelector();
- renderEpisodes(episodes);
- updateCount(episodes.length, episodes.length);
+ renderEpisodes(currentEpisodes);
+ updateEpisodeCount(currentEpisodes.length, currentEpisodes.length);
}
function buildEpisodeSelector() {
episodeSelector.innerHTML = "";
- episodes.forEach((ep) => {
- const code = `S${String(ep.season).padStart(2, "0")}E${String(
- ep.number
- ).padStart(2, "0")}`;
+ currentEpisodes.forEach((ep) => {
+ const code = `S${String(ep.season).padStart(2, "0")}E${String(ep.number).padStart(2, "0")}`;
const op = document.createElement("option");
op.value = ep.id;
op.textContent = `${code} - ${ep.name}`;
@@ -44,51 +98,55 @@ function buildEpisodeSelector() {
}
function renderEpisodes(list) {
- episodesGrid.innerHTML = "";
+ episodesView.classList.remove("hidden");
+ episodesView.innerHTML = "";
list.forEach((ep) => {
- const code = `S${String(ep.season).padStart(2, "0")}E${String(
- ep.number
- ).padStart(2, "0")}`;
-
const card = document.createElement("div");
card.className = "episodeCard";
-
card.innerHTML = `
- ${ep.name} - ${code}
+ ${ep.name}
+ S${String(ep.season).padStart(2, "0")}E${String(ep.number).padStart(2, "0")}
${ep.summary || ""}
`;
- episodesGrid.appendChild(card);
+ episodesView.appendChild(card);
});
}
-function updateCount(n, total) {
- episodeCount.textContent = `Displaying ${n}/${total} episodes.`;
+function updateEpisodeCount(showing, total) {
+ episodeCount.textContent = `Displaying ${showing}/${total} episodes`;
}
function filterEpisodes() {
- const txt = episodeSearch.value.toLowerCase();
-
- const filtered = episodes.filter(
+ const t = episodeSearch.value.toLowerCase();
+ const filtered = currentEpisodes.filter(
(ep) =>
- ep.name.toLowerCase().includes(txt) ||
- (ep.summary && ep.summary.toLowerCase().includes(txt))
+ ep.name.toLowerCase().includes(t) ||
+ ep.summary.toLowerCase().includes(t)
);
-
renderEpisodes(filtered);
- updateCount(filtered.length, episodes.length);
+ updateEpisodeCount(filtered.length, currentEpisodes.length);
}
function jumpToEpisode() {
const id = episodeSelector.value;
- const index = episodes.findIndex((ep) => ep.id == id);
-
- const card = episodesGrid.children[index];
+ const card = document.querySelector(`.episodeCard:nth-child(${currentEpisodes.findIndex(e => e.id == id) + 1})`);
if (card) card.scrollIntoView({ behavior: "smooth" });
}
+function goBack() {
+ episodesView.classList.add("hidden");
+ backBtn.classList.add("hidden");
+ episodeSelector.classList.add("hidden");
+ episodeSearch.classList.add("hidden");
+ episodeCount.classList.add("hidden");
+ showsView.classList.remove("hidden");
+ episodesView.innerHTML = "";
+}
+
+showSearch.addEventListener("input", searchShows);
episodeSearch.addEventListener("input", filterEpisodes);
episodeSelector.addEventListener("change", jumpToEpisode);
-showSelector.addEventListener("change", (e) => loadEpisodes(e.target.value));
+backBtn.addEventListener("click", goBack);
loadShows();
diff --git a/style.css b/style.css
index 228711c..5b5e1c1 100644
--- a/style.css
+++ b/style.css
@@ -1,33 +1,74 @@
-#episodesGrid {
+body {
+ margin: 0;
+ font-family: Arial, Helvetica, sans-serif;
+ background: #f4f4f4;
+}
+
+header {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 15px;
+ padding: 15px;
+ background: #eee;
+ border-bottom: 1px solid #ccc;
+}
+
+header input,
+header select {
+ padding: 6px;
+}
+
+.hidden {
+ display: none;
+}
+
+#showsView,
+#episodesView {
+ padding: 30px;
+ display: flex;
+ flex-direction: column;
+ gap: 40px;
+}
+
+.showCard {
+ background: white;
padding: 25px;
+ border-radius: 10px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
display: grid;
- grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
- justify-items: center;
- gap: 30px;
+ grid-template-columns: 180px 1fr 200px;
+ gap: 25px;
}
-.episodeCard {
- width: 350px;
- background: white;
- border-radius: 12px;
- padding: 20px;
- border: 2px solid #ddd;
- box-shadow: 0 2px 5px rgba(0, 0, 0, 0.08);
+.showCard img {
+ width: 180px;
+ border-radius: 6px;
}
-.episodeCard h2 {
- margin: 0 0 15px 0;
- text-align: center;
- font-size: 20px;
+.showTitle {
+ font-size: 32px;
+ margin: 0 0 10px 0;
}
-.episodeCard img {
- width: 100%;
+.showInfoBox {
+ background: #fafafa;
+ padding: 15px;
border-radius: 8px;
- margin-bottom: 15px;
+ border: 1px solid #ddd;
+ font-size: 14px;
+ line-height: 1.6;
}
-.episodeCard p {
- font-size: 14px;
- line-height: 1.5;
+#episodesView .episodeCard {
+ background: white;
+ border-radius: 10px;
+ padding: 20px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.episodeCard img {
+ width: 100%;
+ max-width: 320px;
+ border-radius: 6px;
}