diff --git a/index.html b/index.html
index 9a400d1..799c4d3 100644
--- a/index.html
+++ b/index.html
@@ -1,21 +1,67 @@
-
+
- TV Show Project | My Name (My GitHub username)
+
+ TV Show Project | Name:Abdu Hassen (GitHub username:Abduhasen)
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/script.js b/script.js
index 87a7de8..803e528 100644
--- a/script.js
+++ b/script.js
@@ -1,12 +1,254 @@
-//You can edit ALL of the code here
-function setup() {
- const allEpisodes = getAllEpisodes();
- makePageForEpisodes(allEpisodes);
+function formatEpisodeCode(episode) {
+ const season = String(episode.season).padStart(2, "0");
+ const number = String(episode.number).padStart(2, "0");
+ return `S${season}E${number}`;
}
-function makePageForEpisodes(episodeList) {
- const rootElem = document.getElementById("root");
- rootElem.textContent = `Got ${episodeList.length} episode(s)`;
+let allShowsCache = null;
+const episodeCache = {};
+
+async function getAllShows() {
+ if (!allShowsCache) {
+ allShowsCache = fetch("https://api.tvmaze.com/shows").then((response) => {
+ if (!response.ok) {
+ throw new Error("Failed to load shows");
+ }
+ return response.json();
+ });
+ }
+ return allShowsCache;
+}
+
+function getEpisodesForShow(showId) {
+ if (!episodeCache[showId]) {
+ episodeCache[showId] = fetch(
+ `https://api.tvmaze.com/shows/${showId}/episodes`,
+ ).then((response) => {
+ if (!response.ok) {
+ throw new Error(`Failed to load episodes for show ID ${showId}`);
+ }
+ return response.json();
+ });
+ }
+ return episodeCache[showId];
+}
+async function setup() {
+ const showsRoot = document.getElementById("shows-root");
+ const showsControls = document.getElementById("shows-controls");
+ const showSearchInput = document.getElementById("show-search-input");
+ const showSearchCount = document.getElementById("show-search-count");
+ const showSelect = document.getElementById("show-select");
+ const episodesRoot = document.getElementById("root");
+ const episodesControls = document.getElementById("episodes-controls");
+ const backToShows = document.getElementById("back-to-shows");
+ const episodeDropdown = document.getElementById("episode-select");
+ const searchInput = document.getElementById("search-input");
+ const searchCount = document.getElementById("search-count");
+ const showsTemplate = document.getElementById("shows-template");
+ const episodesTemplate = document.getElementById("episodes-template");
+ let currentEpisodes = [];
+ let currentShowName = "";
+ try {
+ const allShows = await getAllShows();
+ allShows.sort((a, b) =>
+ a.name.localeCompare(b.name, undefined, {
+ sensitivity: "base",
+ }),
+ );
+ makePageForShows(allShows);
+ updateShowCount(allShows.length);
+ fillShowDropdown(allShows);
+ function updateShowCount(numberOfShows) {
+ showSearchCount.textContent = `Displaying ${numberOfShows}/${allShows.length} shows`;
+ }
+ showSearchInput.addEventListener("input", (event) => {
+ const searchTerm = event.target.value.toLowerCase().trim();
+ const filteredShows = allShows.filter((show) => {
+ const nameMatch = show.name.toLowerCase().includes(searchTerm);
+ const genresMatch = show.genres
+ .join(" ")
+ .toLowerCase()
+ .includes(searchTerm);
+
+ const summaryMatch = show.summary
+ ? show.summary
+ .replace(/<[^>]*>/g, "")
+ .toLowerCase()
+ .includes(searchTerm)
+ : false;
+ return nameMatch || genresMatch || summaryMatch;
+ });
+ makePageForShows(filteredShows);
+ updateShowCount(filteredShows.length);
+ fillShowDropdown(filteredShows);
+ });
+ showSelect.addEventListener("change", (event) => {
+ const selectedShowId = event.target.value;
+
+ if (selectedShowId === "") {
+ return;
+ }
+
+ loadShow(selectedShowId);
+ });
+ async function loadShow(showId) {
+ try {
+ const selectedShow = allShows.find(
+ (show) => show.id === Number(showId),
+ );
+ if (!selectedShow) {
+ return;
+ }
+ currentShowName = selectedShow.name;
+ currentEpisodes = await getEpisodesForShow(showId);
+ searchInput.value = "";
+ fillEpisodeDropdown(currentEpisodes);
+ showsRoot.hidden = true;
+ showsControls.hidden = true;
+ episodesRoot.hidden = false;
+ episodesControls.hidden = false;
+ makePageForEpisodes(currentEpisodes, currentShowName);
+ updateCount(currentEpisodes.length);
+ window.scrollTo(0, 0);
+ } catch (error) {
+ console.error(error);
+ searchCount.textContent = "Error loading episodes.";
+ }
+ }
+ function fillShowDropdown(shows) {
+ showSelect.innerHTML = "";
+
+ const defaultOption = document.createElement("option");
+
+ defaultOption.value = "";
+ defaultOption.textContent = "Select a show";
+
+ showSelect.appendChild(defaultOption);
+
+ shows.forEach((show) => {
+ const option = document.createElement("option");
+
+ option.value = show.id;
+ option.textContent = show.name;
+
+ showSelect.appendChild(option);
+ });
+ }
+ function fillEpisodeDropdown(episodes) {
+ episodeDropdown.innerHTML = ``;
+
+ episodes.forEach((episode) => {
+ const option = document.createElement("option");
+ option.value = episode.id;
+ option.textContent = `${formatEpisodeCode(episode)} - ${episode.name}`;
+ episodeDropdown.appendChild(option);
+ });
+ }
+ function updateCount(numberOfEpisodes) {
+ searchCount.textContent = `Displaying ${numberOfEpisodes}/${currentEpisodes.length} episodes`;
+ }
+ searchInput.addEventListener("input", (event) => {
+ const searchTerm = event.target.value.toLowerCase().trim();
+ episodeDropdown.value = "ALL";
+ const filteredEpisodes = currentEpisodes.filter((episode) => {
+ const nameMatch = episode.name.toLowerCase().includes(searchTerm);
+ const summaryMatch = episode.summary
+ ? episode.summary
+ .replace(/<[^>]*>/g, "")
+ .toLowerCase()
+ .includes(searchTerm)
+ : false;
+ return nameMatch || summaryMatch;
+ });
+ makePageForEpisodes(filteredEpisodes, currentShowName);
+ updateCount(filteredEpisodes.length);
+ });
+ episodeDropdown.addEventListener("change", (event) => {
+ const selectedId = event.target.value;
+ searchInput.value = "";
+ if (selectedId === "ALL") {
+ makePageForEpisodes(currentEpisodes, currentShowName);
+ updateCount(currentEpisodes.length);
+ return;
+ }
+ const selectedEpisode = currentEpisodes.find(
+ (episode) => episode.id === Number(selectedId),
+ );
+ if (selectedEpisode) {
+ makePageForEpisodes([selectedEpisode], currentShowName);
+ updateCount(1);
+ }
+ });
+ backToShows.addEventListener("click", () => {
+ episodesRoot.hidden = true;
+ episodesControls.hidden = true;
+ showsRoot.hidden = false;
+ showsControls.hidden = false;
+ searchInput.value = "";
+ episodeDropdown.value = "ALL";
+ showSelect.value = "";
+ });
+ function makePageForShows(showList) {
+ showsRoot.innerHTML = "";
+ const container = document.createElement("div");
+ container.className = "shows-container";
+ showList.forEach((show) => {
+ const card = showsTemplate.content.cloneNode(true);
+ const button = card.querySelector(".show-button");
+ const title = card.querySelector(".show-title");
+ const image = card.querySelector(".show-image");
+ const summary = card.querySelector(".show-summary");
+ const genres = card.querySelector(".show-genres");
+ const status = card.querySelector(".show-status");
+ const rating = card.querySelector(".show-rating");
+ const runtime = card.querySelector(".show-runtime");
+ title.textContent = show.name;
+ image.src = show.image?.medium || "";
+ image.alt = show.name;
+ summary.innerHTML = show.summary || "No summary available.";
+ genres.textContent = `Genres: ${show.genres.join(", ")}`;
+ status.textContent = `Status: ${show.status}`;
+ rating.textContent = `Rating: ${show.rating?.average || "N/A"}`;
+ runtime.textContent = `Runtime: ${show.runtime || "N/A"} minutes`;
+ button.addEventListener("click", () => {
+ loadShow(show.id);
+ });
+ container.appendChild(card);
+ });
+ showsRoot.appendChild(container);
+ }
+ function makePageForEpisodes(episodeList, showName) {
+ episodesRoot.innerHTML = "";
+ const heading = document.createElement("h1");
+ heading.textContent = `${showName} Episodes`;
+ episodesRoot.appendChild(heading);
+ const container = document.createElement("div");
+ container.className = "episodes-container";
+ episodeList.forEach((episode) => {
+ const card = episodesTemplate.content.cloneNode(true);
+ const title = card.querySelector(".episode-title");
+ const image = card.querySelector(".episode-image");
+ const summary = card.querySelector(".episode-summary");
+ const link = card.querySelector(".episode-link");
+ title.textContent = `${episode.name} - ${formatEpisodeCode(episode)}`;
+ image.src = episode.image?.medium || "";
+ image.alt = episode.name;
+ summary.innerHTML = episode.summary || "";
+ if (episode.url) {
+ link.addEventListener("click", () => {
+ window.open(episode.url, "_blank");
+ });
+ } else {
+ link.style.display = "none";
+ }
+ container.appendChild(card);
+ });
+ episodesRoot.appendChild(container);
+ }
+ } catch (error) {
+ showSearchCount.textContent = "Failed to load TV shows.";
+ console.error(error);
+ }
}
window.onload = setup;
diff --git a/style.css b/style.css
index 77cb8d4..e45240c 100644
--- a/style.css
+++ b/style.css
@@ -1,3 +1,165 @@
+body {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ background-color: #473232;
+}
+[hidden] {
+ display: none !important;
+}
+.search-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 15px;
+ margin: 20px 0;
+ color: #ffffff;
+}
+input[type="text"],
+select {
+ padding: 10px 15px;
+ width: 30%;
+ border-radius: 8px;
+ border: 1px solid rgba(255, 255, 255, 0.3);
+ background-color: #1a0a0a;
+ color: #f5ede0;
+ font-size: 15px;
+ cursor: pointer;
+ outline: none;
+}
+input[type="text"]::placeholder {
+ color: rgba(248, 237, 224, 0.5);
+}
+#search-input,
+#show-search-input {
+ padding: 8px 12px;
+ font-size: 1rem;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+}
+#search-count,
+#show-search-count {
+ color: #ffffff;
+ font-size: 0.95rem;
+}
+#shows-root,
#root {
- color: red;
+ padding: 20px;
+}
+.shows-container,
+.episodes-container {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ gap: 20px;
+}
+.show-card,
+.episode-card {
+ border: 1px solid rgba(255, 255, 255, 0.3);
+ border-radius: 12px;
+ padding: 15px;
+ box-shadow: 0 4px 20px rgba(91, 84, 84, 0.7);
+ background-color: #361a1fb9;
+}
+.show-image,
+.episode-image {
+ width: 100%;
+ border-radius: 8px;
+}
+.show-button {
+ width: 100%;
+ margin-bottom: 20px;
+ padding: 10px;
+ border: none;
+ border-radius: 6px;
+ background-color: #1a0a0a;
+ color: #f5ede0;
+ cursor: pointer;
+}
+.show-button:hover {
+ background-color: #6a1730;
+}
+.show-title {
+ margin: 0;
+ font-size: 22px;
+ text-align: center;
+}
+.show-summary,
+.episode-summary {
+ color: #e8d5c4;
+ font-size: 13px;
+ line-height: 1.6;
+ margin-top: 10px;
+}
+.show-genres,
+.show-status,
+.show-rating,
+.show-runtime {
+ color: #e8d5c4;
+ font-size: 13px;
+}
+.episode-title {
+ border-radius: 6px;
+ padding: 8px;
+ background-color: #1a0a0a;
+ font-size: 15px;
+ margin-bottom: 10px;
+ color: #f5ede0;
+}
+.episode-link,
+#back-to-shows {
+ margin-top: 15px;
+ padding: 8px 12px;
+ border: none;
+ border-radius: 6px;
+ background-color: #1a0a0a;
+ color: #f5ede0;
+ font-size: 14px;
+ cursor: pointer;
+}
+.episode-link:hover,
+#back-to-shows:hover {
+ background-color: #6a1730;
+}
+#shows-root h1,
+#root h1 {
+ color: #e8d5c4;
+ text-align: center;
+}
+footer {
+ color: #e8d5c4;
+ text-align: center;
+ padding: 20px;
+ font-size: 13px;
+ opacity: 0.7;
+}
+footer a {
+ color: #f5ede0;
+}
+
+@media (max-width: 1000px) {
+ .shows-container,
+ .episodes-container {
+ grid-template-columns: repeat(3, 1fr);
+ }
+}
+@media (max-width: 750px) {
+ .shows-container,
+ .episodes-container {
+ grid-template-columns: repeat(2, 1fr);
+ }
+}
+@media (max-width: 500px) {
+ .shows-container,
+ .episodes-container {
+ grid-template-columns: 1fr;
+ }
+
+ .search-container {
+ flex-direction: column;
+ }
+
+ input[type="text"],
+ select {
+ width: 80%;
+ }
}