Skip to content

Commit

Permalink
Merge pull request #98 from Valik3201/refactor/api-functions
Browse files Browse the repository at this point in the history
Refactor/api functions
  • Loading branch information
Valik3201 committed Jan 6, 2024
2 parents 1c4e320 + bd6340f commit ab3f5c8
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 88 deletions.
152 changes: 119 additions & 33 deletions src/js/bookAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,55 +3,141 @@ import axios from 'axios';
import { Notify, Loading } from './notiflixConfig.js';

/**
* The base URL for the books API.
* Base URL for the Books API.
* @type {string}
*/
export const API_URL = 'https://books-backend.p.goit.global/books/';
const API_URL = 'https://books-backend.p.goit.global/books/';

/**
* Fetches books from the specified API endpoint.
* Builds the complete URL for an API endpoint with optional query parameters.
*
* @param {string} endpoint - The endpoint to fetch books from.
* @param {string} [selectedCategory=''] - Optional parameter for filtering books by category.
* @returns {Promise<Object[] | null>} A promise that resolves to an array of book objects or null if an error occurs.
* @param {string} endpoint - The API endpoint.
* @param {Object} params - Optional query parameters.
* @returns {string} The complete URL.
*/
export const fetchBooks = async (endpoint, selectedCategory = '') => {
const buildURL = (endpoint, params = {}) => {
const queryString = Object.keys(params)
.map(key => `${key}=${params[key]}`)
.join('&');

return queryString ? `${API_URL}${endpoint}?${queryString}` : `${API_URL}${endpoint}`;
};

/**
* Fetches the list of categories from the Books API.
*
* @returns {Promise<Array>} A promise that resolves to an array of categories.
*/
export const fetchCategories = async () => {
try {
// Display loading dots while fetching data.
Loading.dots();
const URL = buildURL('category-list');
const response = await axios.get(URL);

// Construct the complete URL based on the endpoint and optional category.
const URL = selectedCategory
? `${API_URL}${endpoint}?category=${selectedCategory}`
: `${API_URL}${endpoint}`;
// Check the HTTP status code and handle accordingly.
handleResponse(response);

return response.data;
} catch (error) {
console.error('Error fetching categories:', error.message);
Notify.failure('Failed to fetch categories. Please try again later.');
return null;
} finally {
Loading.remove(500);
}
};

// Make a GET request using axios.
/**
* Fetches the list of top books from the Books API.
*
* @returns {Promise<Array>} A promise that resolves to an array of top books.
*/
export const fetchTopBooks = async () => {
try {
Loading.dots();
const URL = buildURL('top-books');
const response = await axios.get(URL);

// Check the HTTP status code and handle accordingly.
if (response.status === 200) {
// Return the data received in the response.
return response.data;
} else if (response.status === 404) {
// Handle 404 Not Found
console.error('Error fetching: Not Found');
Notify.failure('Books not found. Please check your search criteria.');
} else if (response.status === 500) {
// Handle 500 Internal Server Error
console.error('Error fetching: Internal Server Error');
Notify.failure('Internal server error. Please try again later.');
} else {
// Handle other status codes if needed
console.error('Error fetching: Unexpected status', response.status);
Notify.failure('An unexpected error occurred. Please try again later.');
}
handleResponse(response);

return response.data;
} catch (error) {
// Log an error message if fetching fails and return null.
console.error('Error fetching:', error.message);
Notify.failure('Failed to fetch books. Please try again later.');
console.error('Error fetching top books:', error.message);
Notify.failure('Failed to fetch top books. Please try again later.');
return null;
} finally {
// Remove loading indicators
Loading.remove(500);
}
};

/**
* Fetches books belonging to a specific category from the Books API.
*
* @param {string} category - The category to filter books.
* @returns {Promise<Array>} A promise that resolves to an array of books.
*/
export const fetchBooksByCategory = async category => {
try {
Loading.dots();
const URL = buildURL('category', { category });
const response = await axios.get(URL);

// Check the HTTP status code and handle accordingly.
handleResponse(response);
return response.data;
} catch (error) {
console.error('Error fetching books by category:', error.message);
Notify.failure('Failed to fetch books by category. Please try again later.');
return null;
} finally {
Loading.remove(500);
}
};

/**
* Fetches a specific book by its ID from the Books API.
*
* @param {string} id - The ID of the book.
* @returns {Promise<Object>} A promise that resolves to the book object.
*/
export const fetchBookById = async id => {
try {
Loading.dots();
const URL = buildURL(id);
const response = await axios.get(URL);

// Check the HTTP status code and handle accordingly.
handleResponse(response);
return response.data;
} catch (error) {
console.error('Error fetching book by ID:', error.message);
Notify.failure('Failed to fetch book by ID. Please try again later.');
return null;
} finally {
Loading.remove(500);
}
};

/**
* Handles the API response based on HTTP status codes.
*
* @param {Object} response - The Axios response object.
*/
const handleResponse = response => {
if (response.status === 200) {
// Do nothing for successful response
} else if (response.status === 404) {
// Handle 404 Not Found
console.error('Error fetching: Not Found');
Notify.failure('Books not found. Please check your search criteria.');
} else if (response.status === 500) {
// Handle 500 Internal Server Error
console.error('Error fetching: Internal Server Error');
Notify.failure('Internal server error. Please try again later.');
} else {
// Handle other status codes if needed
console.error('Error fetching: Unexpected status', response.status);
Notify.failure('An unexpected error occurred. Please try again later.');
}
};
19 changes: 19 additions & 0 deletions src/js/bookMarkup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export const createBookMarkup = ({ _id, title, author, book_image }) => {
const placeholderImageURL = new URL('/src/images/placeholder.jpg', import.meta.url).href;

return `
<div class="books__book" data-book-id="${_id}">
<div class="books__book--cover">
<img class="lazyload"
src="${placeholderImageURL}"
data-src="${book_image}"
alt="${title}">
<div class="books__book--cover-overlay">
<div class="books__book--cover-overlay-text">Quick View</div>
</div>
</div>
<div class="books__book--title">${title}</div>
<div class="books__book--author">${author}</div>
</div>
`;
};
4 changes: 2 additions & 2 deletions src/js/fetchBookById.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { fetchBooks } from './bookAPI.js';
import { fetchBookById } from './bookAPI.js';
import { modalContainer, toggleModal } from './modalHandler.js';
import { bookAddButtonHandler } from './handleBookAddition.js';

Expand All @@ -12,7 +12,7 @@ export const displayBookById = async bookId => {
toggleModal(true);

// Fetch book details using the provided bookId
const bookById = await fetchBooks(`${bookId}`);
const bookById = await fetchBookById(bookId);

// Destructure book details
const { _id, title, author, book_image, description, buy_links } = bookById;
Expand Down
32 changes: 5 additions & 27 deletions src/js/fetchBooks.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { fetchBooks } from './bookAPI.js';
import { fetchTopBooks } from './bookAPI.js';
import { displayBookById } from './fetchBookById.js';
import { topBooksContainer, switchView } from './viewSwitcher.js';
import { createBookMarkup } from './bookMarkup.js';

/**
* Displays top books in the specified HTML container.
Expand All @@ -12,36 +13,13 @@ export const displayTopBooks = async () => {
switchView('topBooks');

// Fetch top books from the API.
const topBooks = await fetchBooks('top-books');

const placeholderImageURL = new URL('/src/images/placeholder.jpg', import.meta.url).href;
const topBooks = await fetchTopBooks();

// Generate markup for each top book category.
const markup = topBooks
.map(category => {
// Generate markup for each book within the category.
const booksMarkup = category.books
.map(({ title, author, book_image, _id }) => {
return `
<div class="books__book" data-book-id="${_id}">
<div class="books__book--cover">
<img class="lazyload"
data-sizes="auto"
src="${placeholderImageURL}"
data-src="${book_image}"
alt="${title}">
<div class="books__book--cover-overlay">
<div class="books__book--cover-overlay-text">Quick View</div>
</div>
</div>
<div class="books__book--title">${title}</div>
<div class="books__book--author">${author}</div>
</div>
`;
})
.join('');

// Combine category title, book markup, and "See more" button into a single category markup.
const booksMarkup = category.books.map(book => createBookMarkup(book)).join('');

return `
<div class="books__category">
<h2 class="books__category--title">${category.list_name}</h2>
Expand Down
26 changes: 4 additions & 22 deletions src/js/fetchBooksByCategories.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { fetchBooks } from './bookAPI.js';
import { fetchBooksByCategory } from './bookAPI.js';
import { displayTopBooks } from './fetchBooks.js';
import { displayBookById } from './fetchBookById.js';
import { topBooksContainer, booksContainer, switchView } from './viewSwitcher.js';
import { createBookMarkup } from './bookMarkup.js';

const displayBooksByCategory = async categoryName => {
const booksByCategory = await fetchBooks('category', categoryName);
const booksByCategory = await fetchBooksByCategory(categoryName);

if (!booksByCategory || !Array.isArray(booksByCategory)) {
console.error('Invalid response from fetchBooks:', booksByCategory);
Expand All @@ -13,27 +14,9 @@ const displayBooksByCategory = async categoryName => {

switchView('category');

const placeholderImageURL = new URL('/src/images/placeholder.jpg', import.meta.url).href;

const booksMarkup = booksByCategory
.flat()
.map(book => {
return `
<div class="books__book" data-book-id="${book._id}">
<div class="books__book--cover">
<img class="lazyload"
src="${placeholderImageURL}"
data-src="${book.book_image}"
alt="${book.title}">
<div class="books__book--cover-overlay">
<div class="books__book--cover-overlay-text">Quick View</div>
</div>
</div>
<div class="books__book--title">${book.title}</div>
<div class="books__book--author">${book.author}</div>
</div>
`;
})
.map(book => createBookMarkup(book))
.join('');

const categoryTitleMarkup = `
Expand All @@ -52,7 +35,6 @@ const displayBooksByCategory = async categoryName => {
books.forEach(book => {
book.addEventListener('click', () => {
const bookId = book.dataset.bookId;
console.log('Displaying book with ID:', bookId);
displayBookById(bookId);
});
});
Expand Down
4 changes: 2 additions & 2 deletions src/js/fetchCategories.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { fetchBooks } from './bookAPI.js';
import { fetchCategories } from './bookAPI.js';

/**
* Displays book categories in the specified HTML container.
Expand All @@ -11,7 +11,7 @@ const displayCategories = async () => {
const categoriesContainer = document.querySelector('.category-list');

// Fetch book categories from the API.
const bookCategories = await fetchBooks('category-list');
const bookCategories = await fetchCategories();

// Sorting categories alphabetically.
bookCategories.sort((a, b) => a.list_name.localeCompare(b.list_name));
Expand Down
4 changes: 2 additions & 2 deletions src/js/localStorage.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { fetchBooks } from './bookAPI.js';
import { fetchBookById } from './bookAPI.js';

//Funkcja dodaj膮ca ksi膮偶k臋 do localStorage
export const addToLocalStorage = async bookId => {
//wykonujemy zapytanie do serwera w celu pobrania danych o ksi膮偶ce
const bookById = await fetchBooks(bookId);
const bookById = await fetchBookById(bookId);

//sprawdzamy czy lacalStorage jest obs艂ugiwane w bie偶膮cej przegl膮darce
if (typeof Storage != 'undefined') {
Expand Down

0 comments on commit ab3f5c8

Please sign in to comment.