Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
118a073
level 0: change the name and github username on hmtl file
Abduhasen Aug 9, 2026
1f19762
level 100:created template elements on html, and wrote a footer on h…
Abduhasen Aug 10, 2026
1b6eb24
Merge pull request #1 from Abduhasen/Data-flow-Projetct-tv-show
Abduhasen Aug 11, 2026
4f7da4b
White text for search box
joanne342 Aug 14, 2026
89b6f96
added search bar and count container
joanne342 Aug 14, 2026
09cb001
added search functionality
joanne342 Aug 14, 2026
d3c61c3
added episode selector
joanne342 Aug 14, 2026
37088da
added episode selector
joanne342 Aug 14, 2026
9c7e957
Merge pull request #3 from joanne342/level200-episode-selector
Abduhasen Aug 15, 2026
8add72e
added fetching API, added statement that tell loading episode, added…
Abduhasen Aug 15, 2026
6017175
Merge pull request #4 from Abduhasen/Data-flow-Projetct-tv-show
Abduhasen Aug 15, 2026
4927cde
Improve episode search functionality
Abduhasen Aug 15, 2026
a2d9c07
Merge pull request #5 from Abduhasen/Data-flow-Projetct-tv-show
Abduhasen Aug 15, 2026
9f59329
Get all TV shows instead of one
joanne342 Aug 16, 2026
b1f2f09
Merge pull request #6 from Abduhasen/level400
Abduhasen Aug 17, 2026
048c076
refactoring code and creating css features
Abduhasen Aug 17, 2026
2976c1b
Merge pull request #7 from Abduhasen/Data-flow-Projetct-tv-show
Abduhasen Aug 17, 2026
0fd9bb5
added a front page, added html element.
Abduhasen Aug 17, 2026
606a8af
removing unnecessary spaces
Abduhasen Aug 17, 2026
6a8d9e8
added dropdown for show list,added html element for the dropdown and …
Abduhasen Aug 17, 2026
f49c525
Merge pull request #8 from Abduhasen/feature/level-500
Abduhasen Aug 17, 2026
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
66 changes: 56 additions & 10 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,21 +1,67 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>TV Show Project | My Name (My GitHub username)</title>
<title>
TV Show Project | Name:Abdu Hassen (GitHub username:Abduhasen)
</title>
<link href="style.css" rel="stylesheet" />
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
</head>

<body>
<div id="root">
</div>

<!-- Loads a provided function called getAllEpisodes() which returns all episodes -->
<script src="episodes.js"></script>

<!-- Loads YOUR javascript code -->
<header id="shows-controls" class="search-container">
<input
type="text"
id="show-search-input"
placeholder="Search shows..."
aria-label="Search shows"
/>
<select id="show-select" aria-label="Select a show">
<option value="">Select a show</option>
</select>
<span id="show-search-count"> Loading shows... </span>
</header>
<main id="shows-root"></main>
<header id="episodes-controls" class="search-container" hidden>
<button id="back-to-shows" type="button">Back to Shows</button>
<select id="episode-select" aria-label="Select an episode"></select>
<input
type="text"
id="search-input"
placeholder="Search episodes..."
aria-label="Search episodes"
/>
<span id="search-count"> Loading episodes... </span>
</header>
<main id="root"></main>
<template id="shows-template">
<article class="show-card">
<button class="show-button" type="button">
<h2 class="show-title"></h2>
</button>
<img class="show-image" src="" alt="" />
<div class="show-summary"></div>
<p class="show-genres"></p>
<p class="show-status"></p>
<p class="show-rating"></p>
<p class="show-runtime"></p>
</article>
</template>
<template id="episodes-template">
<article class="episode-card">
<h2 class="episode-title"></h2>
<img class="episode-image" src="" alt="" />
<div class="episode-summary"></div>
<button class="episode-link" type="button">View on TVMaze</button>
</article>
</template>
<footer>
Data sourced from
<a href="https://tvmaze.com" target="_blank" rel="noopener noreferrer">
TVMaze.com
</a>
</footer>
<script src="script.js"></script>
</body>
</html>
256 changes: 249 additions & 7 deletions script.js
Original file line number Diff line number Diff line change
@@ -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 = `<option value="ALL">All Episodes</option>`;

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;
Loading