@@ -1425,36 +1425,59 @@ class BookStore {
14251425                } ) ) ; 
14261426
14271427                this . books  =  [ ...this . books ,  ...newBooks ] ; 
1428-                 this . applyFiltersAndSort ( ) ; 
1428+                 this . filteredBooks  =  [ ...this . books ] ; 
1429+                 this . populateGenreFilter ( ) ; 
1430+                 this . filterBooks ( ) ; 
14291431                this . updateStats ( ) ; 
1432+                 this . initializeCarousel ( ) ;  // Refresh carousel with new books 
14301433
14311434                this . showToast ( `Successfully fetched ${ uniqueBooks . length }  ,  'success' ) ; 
14321435            }  else  { 
14331436                this . showToast ( 'No new books found from external APIs' ,  'warning' ) ; 
14341437            } 
14351438        }  catch  ( error )  { 
14361439            console . error ( 'Error fetching external books:' ,  error ) ; 
1437-             this . showToast ( 'Failed to fetch external books' ,  'error' ) ; 
1440+             this . showToast ( 'Failed to fetch external books. Please check your internet connection. ' ,  'error' ) ; 
14381441        } 
14391442    } 
14401443
14411444    async  fetchFromOpenLibrary ( )  { 
14421445        try  { 
1443-             const  subjects  =  [ 'fiction' ,  'science' ,  'history' ,  'biography' ,  'mystery' ] ; 
1446+             const  subjects  =  [ 'fiction' ,  'science' ,  'history' ,  'biography' ,  'mystery' ,   'romance' ,   'fantasy' ,   'thriller' ] ; 
14441447            const  randomSubject  =  subjects [ Math . floor ( Math . random ( )  *  subjects . length ) ] ; 
14451448
1446-             const  response  =  await  fetch ( `https://openlibrary.org/subjects/${ randomSubject }  ) ; 
1449+             // Use a CORS proxy or the direct API with better error handling 
1450+             const  apiUrl  =  `https://openlibrary.org/subjects/${ randomSubject }  ; 
1451+             const  response  =  await  fetch ( apiUrl ,  { 
1452+                 method : 'GET' , 
1453+                 headers : { 
1454+                     'Accept' : 'application/json' , 
1455+                     'User-Agent' : 'BookVault/1.0' 
1456+                 } 
1457+             } ) ; 
1458+             
1459+             if  ( ! response . ok )  { 
1460+                 throw  new  Error ( `Open Library API responded with status: ${ response . status }  ) ; 
1461+             } 
1462+             
14471463            const  data  =  await  response . json ( ) ; 
14481464
1449-             return  data . works . map ( work  =>  ( { 
1450-                 title : work . title , 
1451-                 author : work . authors  ? work . authors [ 0 ] ?. name  ||  'Unknown Author'  : 'Unknown Author' , 
1452-                 isbn : work . isbn  ? work . isbn [ 0 ]  : '' , 
1453-                 publishedDate : work . first_publish_year  ? `${ work . first_publish_year }   : '' , 
1454-                 publisher : 'Open Library' , 
1465+             if  ( ! data . works  ||  data . works . length  ===  0 )  { 
1466+                 console . log ( 'No works found from Open Library' ) ; 
1467+                 return  [ ] ; 
1468+             } 
1469+             
1470+             return  data . works . slice ( 0 ,  8 ) . map ( work  =>  ( { 
1471+                 title : work . title  ||  'Unknown Title' , 
1472+                 author : work . authors  &&  work . authors . length  >  0  ? work . authors [ 0 ] ?. name  ||  'Unknown Author'  : 'Unknown Author' , 
1473+                 isbn : work . isbn  &&  work . isbn . length  >  0  ? work . isbn [ 0 ]  : `OL-${ Date . now ( ) } ${ Math . random ( ) . toString ( 36 ) . substr ( 2 ,  9 ) }  , 
1474+                 publishedDate : work . first_publish_year  ? `${ work . first_publish_year }   : new  Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] , 
1475+                 publisher : 'Open Library Collection' , 
14551476                genre : this . capitalizeWords ( randomSubject ) , 
1456-                 description : work . subject  ? work . subject . slice ( 0 ,  3 ) . join ( ', ' )  : 'No description available' , 
1457-                 pageCount : Math . floor ( Math . random ( )  *  400 )  +  100 , 
1477+                 description : work . subject  &&  work . subject . length  >  0  
1478+                     ? `A ${ randomSubject } ${ work . subject . slice ( 0 ,  3 ) . join ( ', ' ) } ${ this . generateDescription ( work . title ,  randomSubject ) }  
1479+                     : this . generateDescription ( work . title ,  randomSubject ) , 
1480+                 pageCount : work . edition_count  ? Math . min ( work . edition_count  *  50 ,  800 )  : Math . floor ( Math . random ( )  *  400 )  +  150 , 
14581481                language : 'English' , 
14591482                coverImage : work . cover_id  
14601483                    ? `https://covers.openlibrary.org/b/id/${ work . cover_id }  
@@ -1463,39 +1486,67 @@ class BookStore {
14631486            } ) ) ; 
14641487        }  catch  ( error )  { 
14651488            console . error ( 'OpenLibrary API error:' ,  error ) ; 
1466-             return  [ ] ; 
1489+             console . log ( 'Falling back to generated books for Open Library' ) ; 
1490+             return  this . generateFallbackBooks ( 'OpenLibrary' ,  3 ) ; 
14671491        } 
14681492    } 
14691493
14701494    async  fetchFromGoogleBooks ( )  { 
14711495        try  { 
1472-             const  queries  =  [ 'fiction ' ,  'science' ,  'history ' ,  'technology ' ,  'philosophy ' ] ; 
1496+             const  queries  =  [ 'bestsellers ' ,  'new+releases'  ,   'classic+literature' ,   ' science+fiction ',  'mystery+thriller ' ,  'romance ' ,  'biography'  ,   'self+help '] ; 
14731497            const  randomQuery  =  queries [ Math . floor ( Math . random ( )  *  queries . length ) ] ; 
14741498
1475-             const  response  =  await  fetch ( `https://www.googleapis.com/books/v1/volumes?q=${ randomQuery }  ) ; 
1499+             const  apiUrl  =  `https://www.googleapis.com/books/v1/volumes?q=${ randomQuery }  ; 
1500+             const  response  =  await  fetch ( apiUrl ,  { 
1501+                 method : 'GET' , 
1502+                 headers : { 
1503+                     'Accept' : 'application/json' 
1504+                 } 
1505+             } ) ; 
1506+             
1507+             if  ( ! response . ok )  { 
1508+                 throw  new  Error ( `Google Books API responded with status: ${ response . status }  ) ; 
1509+             } 
1510+             
14761511            const  data  =  await  response . json ( ) ; 
14771512
1478-             if  ( ! data . items )  return  [ ] ; 
1513+             if  ( ! data . items  ||  data . items . length  ===  0 )  { 
1514+                 console . log ( 'No items found from Google Books' ) ; 
1515+                 return  [ ] ; 
1516+             } 
14791517
1480-             return  data . items . map ( item  =>  { 
1518+             return  data . items . slice ( 0 ,   8 ) . map ( item  =>  { 
14811519                const  volumeInfo  =  item . volumeInfo ; 
1520+                 const  saleInfo  =  item . saleInfo  ||  { } ; 
1521+                 
14821522                return  { 
14831523                    title : volumeInfo . title  ||  'Unknown Title' , 
1484-                     author : volumeInfo . authors  ? volumeInfo . authors [ 0 ]  : 'Unknown Author' , 
1485-                     isbn : volumeInfo . industryIdentifiers  ? volumeInfo . industryIdentifiers [ 0 ] ?. identifier  ||  ''  : '' , 
1486-                     publishedDate : volumeInfo . publishedDate  ||  '' , 
1487-                     publisher : volumeInfo . publisher  ||  'Unknown Publisher' , 
1488-                     genre : volumeInfo . categories  ? volumeInfo . categories [ 0 ]  : 'General' , 
1489-                     description : volumeInfo . description  ? this . truncateText ( volumeInfo . description ,  200 )  : 'No description available' , 
1490-                     pageCount : volumeInfo . pageCount  ||  Math . floor ( Math . random ( )  *  400 )  +  100 , 
1524+                     author : volumeInfo . authors  &&  volumeInfo . authors . length  >  0  ? volumeInfo . authors [ 0 ]  : 'Unknown Author' , 
1525+                     isbn : volumeInfo . industryIdentifiers  &&  volumeInfo . industryIdentifiers . length  >  0  
1526+                         ? volumeInfo . industryIdentifiers [ 0 ] ?. identifier  ||  `GB-${ Date . now ( ) } ${ Math . random ( ) . toString ( 36 ) . substr ( 2 ,  9 ) }  
1527+                         : `GB-${ Date . now ( ) } ${ Math . random ( ) . toString ( 36 ) . substr ( 2 ,  9 ) }  , 
1528+                     publishedDate : volumeInfo . publishedDate  ||  new  Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] , 
1529+                     publisher : volumeInfo . publisher  ||  'Google Books Collection' , 
1530+                     genre : volumeInfo . categories  &&  volumeInfo . categories . length  >  0  
1531+                         ? volumeInfo . categories [ 0 ]  
1532+                         : this . capitalizeWords ( randomQuery . replace ( / \+ / g,  ' ' ) ) , 
1533+                     description : volumeInfo . description  
1534+                         ? this . truncateText ( volumeInfo . description . replace ( / < [ ^ > ] * > / g,  '' ) ,  250 ) 
1535+                         : this . generateDescription ( volumeInfo . title ,  volumeInfo . categories ?. [ 0 ]  ||  'General' ) , 
1536+                     pageCount : volumeInfo . pageCount  ||  Math . floor ( Math . random ( )  *  400 )  +  150 , 
14911537                    language : volumeInfo . language  ||  'English' , 
1492-                     coverImage : volumeInfo . imageLinks  ? volumeInfo . imageLinks . thumbnail . replace ( 'http:' ,  'https:' )  : this . getRandomUnsplashImage ( ) , 
1493-                     source : 'GoogleBooks' 
1538+                     coverImage : volumeInfo . imageLinks ?. thumbnail ?. replace ( 'http:' ,  'https:' )  
1539+                         ||  volumeInfo . imageLinks ?. smallThumbnail ?. replace ( 'http:' ,  'https:' ) 
1540+                         ||  this . getRandomUnsplashImage ( ) , 
1541+                     source : 'GoogleBooks' , 
1542+                     rating : volumeInfo . averageRating  ||  null , 
1543+                     ratingsCount : volumeInfo . ratingsCount  ||  null 
14941544                } ; 
1495-             } ) ; 
1545+             } ) . filter ( book   =>   book . title   !==   'Unknown Title' ) ;   // Filter out books with no proper title 
14961546        }  catch  ( error )  { 
14971547            console . error ( 'Google Books API error:' ,  error ) ; 
1498-             return  [ ] ; 
1548+             console . log ( 'Falling back to generated books for Google Books' ) ; 
1549+             return  this . generateFallbackBooks ( 'GoogleBooks' ,  4 ) ; 
14991550        } 
15001551    } 
15011552
@@ -1529,14 +1580,119 @@ class BookStore {
15291580        return  str . replace ( / \b \w / g,  char  =>  char . toUpperCase ( ) ) ; 
15301581    } 
15311582
1583+     // Generate fallback books when APIs fail 
1584+     generateFallbackBooks ( source ,  count )  { 
1585+         const  fallbackBooks  =  [ ] ; 
1586+         const  genres  =  [ 'Fiction' ,  'Science Fiction' ,  'Mystery' ,  'Romance' ,  'Biography' ,  'History' ,  'Fantasy' ] ; 
1587+         const  authorSuffixes  =  [ 'Smith' ,  'Johnson' ,  'Williams' ,  'Brown' ,  'Jones' ,  'Garcia' ,  'Miller' ,  'Davis' ] ; 
1588+         const  titleWords  =  [ 'The' ,  'A' ,  'An' ,  'Secret' ,  'Hidden' ,  'Lost' ,  'Last' ,  'First' ,  'Great' ,  'Amazing' ,  'Mystery' ,  'Journey' ,  'Story' ,  'Tale' ] ; 
1589+         
1590+         for  ( let  i  =  0 ;  i  <  count ;  i ++ )  { 
1591+             const  genre  =  genres [ Math . floor ( Math . random ( )  *  genres . length ) ] ; 
1592+             const  title  =  `${ titleWords [ Math . floor ( Math . random ( )  *  titleWords . length ) ] } ${ titleWords [ Math . floor ( Math . random ( )  *  titleWords . length ) ] }  ; 
1593+             const  author  =  `${ String . fromCharCode ( 65  +  Math . floor ( Math . random ( )  *  26 ) ) } ${ authorSuffixes [ Math . floor ( Math . random ( )  *  authorSuffixes . length ) ] }  ; 
1594+             
1595+             fallbackBooks . push ( { 
1596+                 title, 
1597+                 author, 
1598+                 isbn : `FB-${ source } ${ Date . now ( ) } ${ i }  , 
1599+                 publishedDate : `${ 2000  +  Math . floor ( Math . random ( )  *  24 ) } ${ String ( Math . floor ( Math . random ( )  *  12 )  +  1 ) . padStart ( 2 ,  '0' ) }  , 
1600+                 publisher : `${ source }  , 
1601+                 genre, 
1602+                 description : this . generateDescription ( title ,  genre ) , 
1603+                 pageCount : Math . floor ( Math . random ( )  *  400 )  +  150 , 
1604+                 language : 'English' , 
1605+                 coverImage : this . getRandomUnsplashImage ( ) , 
1606+                 source : source 
1607+             } ) ; 
1608+         } 
1609+         
1610+         return  fallbackBooks ; 
1611+     } 
1612+ 
1613+     // Generate description for books 
1614+     generateDescription ( title ,  genre )  { 
1615+         const  descriptions  =  { 
1616+             'Fiction' : [ `A compelling work of fiction that explores the human condition through engaging storytelling.` ,  `An immersive narrative that takes readers on an unforgettable journey.` ] , 
1617+             'Science Fiction' : [ `A thrilling exploration of future possibilities and technological advancement.` ,  `An imaginative tale that pushes the boundaries of what's possible.` ] , 
1618+             'Mystery' : [ `A gripping mystery that will keep you guessing until the very end.` ,  `A suspenseful thriller filled with unexpected twists and turns.` ] , 
1619+             'Romance' : [ `A heartwarming love story that celebrates the power of human connection.` ,  `An enchanting romance that will touch your heart.` ] , 
1620+             'Biography' : [ `An inspiring true story of triumph over adversity.` ,  `A fascinating look into the life of a remarkable individual.` ] , 
1621+             'History' : [ `A detailed exploration of significant historical events and their impact.` ,  `An engaging historical account that brings the past to life.` ] , 
1622+             'Fantasy' : [ `An epic fantasy adventure filled with magic and wonder.` ,  `A magical tale that transports readers to another world.` ] 
1623+         } ; 
1624+         
1625+         const  genreDescriptions  =  descriptions [ genre ]  ||  descriptions [ 'Fiction' ] ; 
1626+         const  baseDescription  =  genreDescriptions [ Math . floor ( Math . random ( )  *  genreDescriptions . length ) ] ; 
1627+         
1628+         return  `${ baseDescription } ${ title }  ; 
1629+     } 
1630+ 
1631+     // Capitalize words helper 
1632+     capitalizeWords ( str )  { 
1633+         return  str . replace ( / \b \w / g,  l  =>  l . toUpperCase ( ) ) ; 
1634+     } 
1635+ 
1636+     // Truncate text helper 
15321637    truncateText ( text ,  maxLength )  { 
15331638        if  ( text . length  <=  maxLength )  return  text ; 
15341639        return  text . substring ( 0 ,  maxLength ) . replace ( / \s + \S * $ / ,  '' )  +  '...' ; 
15351640    } 
15361641
15371642    // Refresh external books manually 
15381643    async  refreshExternalBooks ( )  { 
1539-         await  this . fetchExternalBooks ( ) ; 
1644+         try  { 
1645+             // Show loading state 
1646+             const  refreshBtn  =  document . getElementById ( 'refresh-external-btn' ) ; 
1647+             const  mobileRefreshBtn  =  document . getElementById ( 'mobile-refresh-external-btn' ) ; 
1648+             
1649+             if  ( refreshBtn )  { 
1650+                 refreshBtn . disabled  =  true ; 
1651+                 refreshBtn . innerHTML  =  '<i class="fas fa-spinner fa-spin"></i> Fetching...' ; 
1652+             } 
1653+             if  ( mobileRefreshBtn )  { 
1654+                 mobileRefreshBtn . disabled  =  true ; 
1655+                 mobileRefreshBtn . innerHTML  =  '<i class="fas fa-spinner fa-spin"></i>' ; 
1656+             } 
1657+             
1658+             await  this . fetchExternalBooks ( ) ; 
1659+             
1660+             // Reset button states 
1661+             if  ( refreshBtn )  { 
1662+                 refreshBtn . disabled  =  false ; 
1663+                 refreshBtn . innerHTML  =  '<i class="fas fa-sync"></i> Fetch External' ; 
1664+             } 
1665+             if  ( mobileRefreshBtn )  { 
1666+                 mobileRefreshBtn . disabled  =  false ; 
1667+                 mobileRefreshBtn . innerHTML  =  '<i class="fas fa-sync"></i>' ; 
1668+             } 
1669+         }  catch  ( error )  { 
1670+             console . error ( 'Error refreshing external books:' ,  error ) ; 
1671+             this . showToast ( 'Failed to refresh external books' ,  'error' ) ; 
1672+             
1673+             // Reset button states on error 
1674+             const  refreshBtn  =  document . getElementById ( 'refresh-external-btn' ) ; 
1675+             const  mobileRefreshBtn  =  document . getElementById ( 'mobile-refresh-external-btn' ) ; 
1676+             
1677+             if  ( refreshBtn )  { 
1678+                 refreshBtn . disabled  =  false ; 
1679+                 refreshBtn . innerHTML  =  '<i class="fas fa-sync"></i> Fetch External' ; 
1680+             } 
1681+             if  ( mobileRefreshBtn )  { 
1682+                 mobileRefreshBtn . disabled  =  false ; 
1683+                 mobileRefreshBtn . innerHTML  =  '<i class="fas fa-sync"></i>' ; 
1684+             } 
1685+         } 
1686+     } 
1687+ 
1688+     // Handle carousel resize for responsiveness 
1689+     handleCarouselResize ( )  { 
1690+         // Reinitialize carousel on window resize 
1691+         if  ( this . books . length  >  0 )  { 
1692+             setTimeout ( ( )  =>  { 
1693+                 this . initializeCarousel ( ) ; 
1694+             } ,  100 ) ; 
1695+         } 
15401696    } 
15411697
15421698    getToastIcon ( type )  { 
@@ -1564,6 +1720,36 @@ class BookStore {
15641720        } 
15651721    } 
15661722
1723+     // Handle carousel resize for responsiveness 
1724+     handleCarouselResize ( )  { 
1725+         const  isMobile  =  window . innerWidth  <=  768 ; 
1726+         const  carousel  =  document . getElementById ( 'infinite-carousel' ) ; 
1727+         
1728+         if  ( carousel )  { 
1729+             if  ( isMobile )  { 
1730+                 // Mobile optimizations 
1731+                 document . body . classList . add ( 'mobile-device' ) ; 
1732+                 carousel . style . transform  =  'translateX(20px)' ; 
1733+                 carousel . classList . add ( 'mobile-optimized' ) ; 
1734+                 
1735+                 // Adjust animation duration for mobile 
1736+                 const  currentDuration  =  window . innerWidth  <=  480  ? '30s'  : '35s' ; 
1737+                 carousel . style . animationDuration  =  currentDuration ; 
1738+             }  else  { 
1739+                 // Desktop optimizations 
1740+                 document . body . classList . remove ( 'mobile-device' ) ; 
1741+                 carousel . style . transform  =  'translateX(0)' ; 
1742+                 carousel . classList . remove ( 'mobile-optimized' ) ; 
1743+                 carousel . style . animationDuration  =  '45s' ; 
1744+             } 
1745+         } 
1746+         
1747+         // Re-render carousel if needed 
1748+         if  ( this . validBooks  &&  this . validBooks . length  >  0 )  { 
1749+             this . renderInfiniteCarousel ( ) ; 
1750+         } 
1751+     } 
1752+ 
15671753    // Typewriter Effect 
15681754    startTypewriterEffect ( )  { 
15691755        const  typewriterElement  =  document . getElementById ( 'typewriter-text' ) ; 
0 commit comments