diff --git a/index.html b/index.html index cad7e22..38fc1cc 100644 --- a/index.html +++ b/index.html @@ -1,5 +1,4 @@ -
@@ -9,12 +8,13 @@ - + - - - - + - + diff --git a/script.js b/script.js index 87a7de8..f75312f 100644 --- a/script.js +++ b/script.js @@ -1,12 +1,272 @@ -//You can edit ALL of the code here -function setup() { - const allEpisodes = getAllEpisodes(); - makePageForEpisodes(allEpisodes); +const currentState = { + showsFetched: false, + episodesFetched: false, + allShows: null, + showSelected: null, + allEpisodes: null, + showsID: "", +}; + +const showDropDown = document.querySelector("#series__dropdown"); +const episodeDropDown = document.querySelector("#filter__dropdown"); +const displayingFilterResults = document.createElement("p"); + +// Fetch API function to fetch data upon call +async function fetchAPI(url) { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Could not fetch data from ${url}`); + } + return response.json(); +} + +// Fetches shows data via API +async function getAllShows() { + try { + if (!currentState.showsFetched) { + currentState.allShows = await fetchAPI("https://api.tvmaze.com/shows"); + currentState.showsFetched = true; + } + return currentState.allShows; + } catch (error) { + console.error(error); + } +} + +async function getAllEpisodes() { + try { + if (!currentState.episodesFetched) { + currentState.allEpisodes = await fetchAPI( + `https://api.tvmaze.com/shows/${currentState.showsID}/episodes` + ); + currentState.episodesFetched = true; + } + return currentState.allEpisodes; + } catch (error) { + console.error(error); + } +} + +// // Creating cards + +// // Creates element and adds class if required +function createElement(tag, className) { + const element = document.createElement(tag); + if (className) { + element.classList.add(className); + } + return element; +} + +// Adds padding to the start of season and episode +// Example --- S1E4 = S01E04 +function formatSeasonAndEpisode(season, episodeNumber) { + return `S${String(season).padStart(2, "0")}E${String(episodeNumber).padStart( + 2, + "0" + )}`; +} + +// Creates card component +function createEpisodeCard(episode) { + const cardDiv = createElement("section", "card"); + + const layoutSelector = document.querySelector(".layout"); + + const seasonAndEpisodeWithPadding = formatSeasonAndEpisode( + episode.season, + episode.number + ); + + const filmTitleElement = createElement("h1", "card__title"); + filmTitleElement.textContent = `${episode.name}`; + if (episode.season) { + filmTitleElement.textContent = `${episode.name} - ${seasonAndEpisodeWithPadding}`; + } + cardDiv.appendChild(filmTitleElement); + + const filmImgElement = createElement("img", "card__img"); + if (episode.image && episode.image.original) { + filmImgElement.setAttribute("src", episode.image.original); + } else { + filmImgElement.setAttribute( + "src", + "https://t3.ftcdn.net/jpg/04/34/72/82/360_F_434728286_OWQQvAFoXZLdGHlObozsolNeuSxhpr84.jpg" + ); + } + cardDiv.appendChild(filmImgElement); + + const filmSummaryElement = createElement("p", "card__summary"); + filmSummaryElement.innerHTML = episode.summary; + cardDiv.appendChild(filmSummaryElement); + + layoutSelector.appendChild(cardDiv); + + return cardDiv; +} + +async function searchShowsDropDown() { + try { + const showsData = await getAllShows(); + const defaultOptionShow = document.createElement("option"); + defaultOptionShow.textContent = "Select show..."; + showDropDown.appendChild(defaultOptionShow); + + showsData.forEach((show) => { + const listOfShows = document.createElement("option"); + listOfShows.textContent = `${show.name}`; + showDropDown.appendChild(listOfShows); + }); + + showDropDown.addEventListener("change", async function (event) { + if (event.target.value === defaultOptionShow.textContent) { + clearCards(); + render(); + episodeDropDown.innerHTML = ""; + return; + } + + currentState.showSelected = showsData.find( + (show) => show.name === event.target.value + ); + currentState.showsID = currentState.showSelected.id; + + searchEpisodesDropDown(); + }); + } catch (error) { + console.error(error); + } +} + +async function searchEpisodesDropDown() { + let episodes = await getAllEpisodes(); + clearCards(); + await makePageForEpisodes(episodes); + episodeDropDown.innerHTML = ""; + + // Filtering episodes + let defaultForEpisodes = document.createElement("option"); + defaultForEpisodes.textContent = "Select Episode..."; + episodeDropDown.appendChild(defaultForEpisodes); + + episodes.forEach((episode) => { + let listOfEpisodes = document.createElement("option"); + listOfEpisodes.textContent = `${episode.name} - ${formatSeasonAndEpisode( + episode.season, + episode.number + )}`; + episodeDropDown.appendChild(listOfEpisodes); + }); + + episodeDropDown.addEventListener("change", async function (event) { + const dash = event.target.value.indexOf("-"); + const selectedEpisode = event.target.value.slice(0, dash - 1); + const filteredEpisode = episodes.filter( + (episode) => episode.name === selectedEpisode + ); + clearCards(); + await makePageForEpisodes(filteredEpisode); + + if (event.target.value === defaultForEpisodes.textContent) { + clearCards(); + await makePageForEpisodes(episodes); + } + }); + + currentState.episodesFetched = false; +} + +const searchField = document.querySelector("#search__field"); +searchField.addEventListener("input", searchFilterEpisodes); + +async function searchFilterEpisodes() { + const showsData = await getAllShows(); + const showsFilter = showsData.filter( + (show) => + show.name.toLowerCase().includes(searchField.value.toLowerCase()) || + show.summary.toLowerCase().includes(searchField.value.toLowerCase()) + ); + + if ((searchField.textContent = "")) { + displayingFilterResults.textContent = ""; + } else { + displayingFilterResults.textContent = `Displaying ${showsFilter.length}/${showsData.length} shows`; + } + + const searchLayout = document.querySelector(".search__layout"); + searchLayout.appendChild(displayingFilterResults); + + clearCards(); + makePageForEpisodes(showsFilter); +} + +searchFilterEpisodes(); + +async function makePageForEpisodes(episodeList) { + const layoutSelector = document.querySelector(".layout"); + // Creates a card for each episode + episodeList.forEach((episode) => { + const filmCard = createEpisodeCard(episode); + layoutSelector.appendChild(filmCard); + + filmCard.addEventListener("click", async function () { + displayingFilterResults.textContent = ""; + currentState.showsID = episode.id; + currentState.showSelected = episode; + const episodeData = await getAllEpisodes(); + const selectedShow = [...showDropDown].find( + (show) => show.textContent === currentState.showSelected.name + ); + selectedShow.selected = true; + clearCards(); + makePageForEpisodes(episodeData); + episodeData.forEach((episode) => { + let listOfEpisodes = document.createElement("option"); + listOfEpisodes.textContent = `${ + episode.name + } - ${formatSeasonAndEpisode(episode.season, episode.number)}`; + episodeDropDown.appendChild(listOfEpisodes); + }); + + episodeDropDown.addEventListener("change", async function (event) { + const dash = event.target.value.indexOf("-"); + const selectedEpisode = event.target.value.slice(0, dash - 1); + const filteredEpisode = episodeData.filter( + (episode) => episode.name === selectedEpisode + ); + clearCards(); + await makePageForEpisodes(filteredEpisode); + }); + }); + }); +} + +// Creates footer +function createFooter() { + const footerElement = createElement("footer", "footer"); + const layoutSelector = document.querySelector(".layout"); + + footerElement.innerHTML = `Data originally sourced by + TVMaze.com`; + layoutSelector.insertAdjacentElement("afterend", footerElement); +} + +function clearCards() { + const layoutSelector = document.querySelector(".layout"); + layoutSelector.innerHTML = ""; } -function makePageForEpisodes(episodeList) { - const rootElem = document.getElementById("root"); - rootElem.textContent = `Got ${episodeList.length} episode(s)`; +async function render() { + try { + await getAllShows().then((allEpisodes) => { + makePageForEpisodes(allEpisodes); + }); + await searchShowsDropDown(); + // await searchEpisodesDropDown(); + // createFooter(); + } catch (error) { + console.error(error); + } } -window.onload = setup; +window.onload = render; diff --git a/style.css b/style.css index 77cb8d4..13c72a2 100644 --- a/style.css +++ b/style.css @@ -1,3 +1,142 @@ -#root { +@import url("https://fonts.googleapis.com/css2?family=Fira+Sans:wght@100;200;300;400;500&display=swap"); + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +:root { + --background-color: #264653; + --layout-padding: 3rem; + --font-color: #d4ebe9; + --cards-layout: repeat(auto-fill, minmax(28rem, auto)); + --layout-for-card: 6rem 18rem auto; + --font: "Fira Sans", sans-serif; +} + +a { + text-decoration: none; + color: var(--font-color); +} + +@keyframes pageLoad { + from { + width: 40%; + } + to { + width: 100%; + } +} + +body { + background: var(--background-color); + font-family: var(--font); + color: var(--font-color); + animation-name: pageLoad; + animation-duration: 1s; + animation-timing-function: cubic-bezier(0.39, 0.58, 0.57, 1); +} + +.search__layout { + padding: 0 var(--layout-padding); + margin-top: 7rem; + width: max-content; + display: flex; + align-items: center; + gap: 2rem; +} + +#series__dropdown { + width: 18rem; + height: 2.6rem; + padding-left: 0.4rem; + font-size: 1.2rem; +} + +#filter__dropdown { + width: 18rem; + height: 2.6rem; + padding-left: 0.4rem; + font-size: 1.2rem; +} + +#search__field { + width: 18rem; + height: 2.6rem; + padding-left: 0.4rem; + font-size: 1.2rem; +} + +.display__episodes { color: red; } + +.layout { + display: grid; + grid-template-columns: var(--cards-layout); + gap: 2rem; + padding: 6rem var(--layout-padding); +} + +.card { + display: grid; + grid-template-rows: var(--layout-for-card); + justify-content: center; + border: 1px solid #d4ebe938; + border-radius: 1.4rem; + padding: 0.4rem; + box-shadow: 0px 8px 20px -7.8px #00000047; + opacity: 0.6; + transition: all ease-in-out 0.4s; + overflow: hidden; + cursor: pointer; +} + +.card:hover, +.card:active { + opacity: 1; + box-shadow: 0px 8px 20px -2px #00000047; +} + +.card__title { + font-size: 1.6rem; + text-align: center; + letter-spacing: 0.1rem; + font-weight: 400; + align-self: center; +} + +.card__img { + height: 14rem; + margin: 0.6rem; + border-radius: 16px; + box-shadow: 2px 5px 10px 0px #00000026; + justify-self: center; +} + +.card__summary { + padding: 0.8rem; + font-weight: 300; + line-height: 1.8rem; + letter-spacing: 0.1rem; +} + +.footer { + padding: 2.6rem 0.6rem; + letter-spacing: 0.1rem; + font-weight: 100; + margin: 0 var(--layout-padding); + border-top: 2px solid #67bd76; +} + +#tv-maze-link { + color: #67bd76; + border-bottom: 2px solid #67bd75; + padding-bottom: 0.2rem; +} + +#tv-maze-link:hover, +#tv-maze-link:active { + font-weight: 500; +}