Here is the solution for the 1st Challenge
There is more than one specific way to complete the exercise and meet the requirements.
You may have chosen to complete the task differently, that's okay.
Compare your solution to the one presented below to see if there is room to refactor your code based on the ideas below.
ts
// We save these common references to use in multiple functionsconstform =document .getElementById ("search-form") asHTMLFormElement ;constcart =document .querySelector ("#cart .cart-items") asHTMLUListElement ;window .onload = () => {// Register a function on page load to handle the form submitform .onsubmit =handleForm ;};// Define a structure for a Book// This is Optional: not technically required to complete the exerciseinterfaceBook {key : string;title : string;publisher ?: string[];author_name ?: string;first_publish_year ?: number;isbn ?: string[];cover_i ?: string;number_of_pages_median ?: number;}// Our function to handle the form submissionasync functionhandleForm (e :Event ):Promise <void> {// Stop the form from submtting traditionallye .preventDefault ();// Get the value of the search input fieldconstsearchField =form .querySelector ("input[name='search']") asHTMLInputElement ;// Trim away whitespacesconstsearchTerm =searchField .value .trim ();if (searchTerm .length > 0) {// Set the Submit button and input field to loading statetoggleLoadingState (true);try {constbooks = awaitfetchBooks (searchTerm );displayBooks (books );} catch (error ) {showError ("Failed to fetch books. Please try again.");} finally {toggleLoadingState (false);}}}async functionfetchBooks (searchTerm : string):Promise <Book []> {// Make a call to the OpenLibrary APIconstresponse = awaitfetch (`https://openlibrary.org/search.json?q=${searchTerm }`);if (!response .ok ) {throw newError ("Network response was not ok");}constdata = awaitresponse .json ();returndata .docs asBook [];}functiontoggleLoadingState (isLoading : boolean) {constsubmitButton =form .querySelector ("button[type='submit']") asHTMLButtonElement ;constresults =document .getElementById ("results") asHTMLElement ;constsearchField =form .querySelector ("input[name='search']") asHTMLInputElement ;searchField .disabled =isLoading ;submitButton .disabled =isLoading ;submitButton .setAttribute ("aria-busy",isLoading .toString ());submitButton .innerHTML =isLoading ? "Loading..." : "Search";results .setAttribute ("aria-busy",isLoading .toString ());}functiondisplayBooks (books :Book []) {constresults =document .getElementById ("results") asHTMLElement ;results .innerHTML = "";if (books .length === 0) {results .innerHTML = "No results found.";return;}books .forEach ((book ) => {constbookElement =createBookElement (book , "results");results .appendChild (bookElement );});}// Common function to handle DOM manipulation for results and cart sectionfunctioncreateBookElement (book :Book ,location : "results" | "cart"):HTMLElement {constbookElement =location === "cart"?document .createElement ("li"):document .createElement ("article");bookElement .className =location === "cart" ? "item" : "book";constcoverBox =document .createElement ("div");coverBox .className = "cover";constimage =document .createElement ("img");image .src =book .cover_i ? `https://covers.openlibrary.org/b/id/${book .cover_i }-M.jpg`: "/public/no-image.svg";image .alt =book .title ;coverBox .appendChild (image );bookElement .appendChild (coverBox );constinfo =document .createElement ("div");info .className = "info";consttitle =location === "cart"?document .createElement ("p"):document .createElement ("h3");title .className = "title";title .innerHTML = `<a href="https://openlibrary.org${book .key }">${book .title }</a>`;info .appendChild (title );constauthor =document .createElement ("p");author .className = "author";author .innerHTML =book .author_name ? `- ${book .author_name }`: "- Unknown Author";info .appendChild (author );if (location === "results") {constisbn =document .createElement ("p");isbn .className = "isbn";isbn .innerHTML = `ISBN: ${book .isbn ?.[0] || "N/A"}`;info .appendChild (isbn );constmeta =document .createElement ("p");meta .className = "meta";meta .innerText = `${book .publisher ?.[0] || ""}, ${book .first_publish_year || ""}, ${book .number_of_pages_median ?book .number_of_pages_median + " pages" : ""}`;info .appendChild (meta );// Results have an 'Add to Cart' functionalityconstbutton =document .createElement ("button");button .innerHTML = "Add to Cart";button .setAttribute ("data-book-id",book .key );button .addEventListener ("click", () =>addToCart (book ,button ));info .appendChild (button );} else {// cart has a remove buttonconstremove =document .createElement ("button");remove .className = "remove";remove .innerHTML = "Remove";remove .addEventListener ("click", () => {bookElement .remove ();constaddToCartButton =document .querySelector (`#results button[data-book-id="${book .key }"]`) asHTMLButtonElement ;if (addToCartButton ) {addToCartButton .disabled = false;addToCartButton .innerHTML = "Add to Cart";}countCartItems ();});info .appendChild (remove );}bookElement .appendChild (info );returnbookElement ;}functionaddToCart (book :Book ,button :HTMLButtonElement ) {constitem =createBookElement (book , "cart");// Create a cart item: i.e. without ISBN and meta fieldscart ?.appendChild (item );button .disabled = true;button .innerHTML = "Added...";// Remove the 'Your Cart is Empty' message, if it existsconstempty =document .querySelector (".empty") asHTMLLIElement ;if (empty )empty .remove ();// Enable the Cart Actionsconstbuttons =document .querySelectorAll ("#cart button") asNodeListOf <HTMLButtonElement >;buttons .forEach ((btn ) => (btn .disabled = false));// Update the Count of items in the cartcountCartItems ();}functioncountCartItems () {constcartCount =document .querySelector ("label#cart-icon");if (cartCount ) {constitems =document .querySelectorAll ("#cart li.item");cartCount .setAttribute ("data-count",items .length .toString ());}}functionclearCart () {constcart =document .querySelector ("#cart .cart-items") asHTMLUListElement ;if (cart ) {// Replace the 'Your Cart is Empty' messagecart .innerHTML = "<li class='empty'>Your Cart is Empty!</li>";// Disable the Cart Actions since it's emptyconstbuttons =document .querySelectorAll ("#cart button") asNodeListOf <HTMLButtonElement >;buttons .forEach ((button ) => (button .disabled = true));// Remove the 'Added to Cart' message on the results// and enable the Add actionconstaddToCartButtons =document .querySelectorAll ("#results button") asNodeListOf <HTMLButtonElement >;addToCartButtons .forEach ((button ) => {button .disabled = false;button .innerHTML = "Add to Cart";});// Update the count of the cartcountCartItems ();} else {showError ("Cart not found.");}}functionshowError (message : string) {constresults =document .getElementById ("results") asHTMLElement ;results .innerHTML = `<p class="error">${message }</p>`;console .error (message );}// REQUEST MODALconstdialog =document .querySelector ("dialog#request-modal") asHTMLDialogElement ;constshowButton =document .querySelector ("button#request-books") asHTMLButtonElement ;constcloseButton =document .querySelector ("button#close-request") asHTMLButtonElement ;showButton .addEventListener ("click", () => {dialog .showModal ();// Get a list of all the items from the cart and copy it to the Request Books modalconstrequestModal =document .querySelector ("#request-modal ul") asHTMLUListElement ;requestModal .innerHTML =cart .innerHTML ;// Empty the cartclearCart ();});closeButton .addEventListener ("click", () => {dialog .close ();});