diff --git a/.gitignore b/.gitignore index 2b76d7c..cb04f0c 100644 --- a/.gitignore +++ b/.gitignore @@ -156,3 +156,7 @@ dist vite.config.js.timestamp-* vite.config.ts.timestamp-* +node_modules/ +package-lock.json +.env +.DS_store diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..b2b47dc --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "es5", + "printWidth": 80 +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..32db0e6 --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "c55-core-week-6", + "version": "1.0.0", + "description": "The week 6 assignment for the HackYourFuture Core program can be found at the following link: https://hub.hackyourfuture.nl/core-program-week-6-assignment", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/hannahwn/c55-core-week-6.git" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "commonjs", + "bugs": { + "url": "https://github.com/hannahwn/c55-core-week-6/issues" + }, + "homepage": "https://github.com/hannahwn/c55-core-week-6#readme", + "dependencies": { + "chalk": "^4.1.2" + } +} diff --git a/reading-list-manager/app.js b/reading-list-manager/app.js index b0365ef..9532ba1 100644 --- a/reading-list-manager/app.js +++ b/reading-list-manager/app.js @@ -2,12 +2,26 @@ // node app.js // TODO: Implement the main application logic here -// 1. Load books on startup -// 2. Display all books -// 3. Show summary statistics -// 4. Add example of filtering by genre or read/unread status -// 5. Add example of marking a book as read +const readingList = require('./readingList'); +const chalk = require('chalk'); console.log('📚 MY READING LIST 📚\n'); // Your implementation here +function main() { + console.log(chalk.blue('=== MY READING LIST ===')); + + // 1. Load books on startup + let myBooks = readingList.loadBooks(); + // 2. Display all books + readingList.printAllBooks(myBooks); + // 3. Show summary statistics + readingList.printSummary(myBooks); + // 4. Add example of filtering by genre or read/unread status + readingList.getBooksByGenre(myBooks, 'Fiction'); + // 5. Add example of marking a book as read + console.log(chalk.blue('/n === Marking a book as read ===')); + myBooks = readingList.markAsRead(myBooks, 7); +} + +main(); diff --git a/reading-list-manager/books.json b/reading-list-manager/books.json index 0637a08..0d29e49 100644 --- a/reading-list-manager/books.json +++ b/reading-list-manager/books.json @@ -1 +1,51 @@ -[] \ No newline at end of file +[ + { + "id": 1, + "title": "1984", + "author": "George Orwell", + "genre": "Fiction", + "read": false + }, + { + "id": 2, + "title": "The Havoc of Choice", + "author": "Wanjiru Koinange", + "genre": "African Literature", + "read": true + }, + { + "id": 3, + "title": "CreepShow", + "author": "Stephen King", + "genre": "Comic", + "read": false + }, + { + "id": 4, + "title": "Fire and Ice", + "author": "Erin Hunter", + "genre": "Fiction", + "read": true + }, + { + "id": 5, + "title": "Toxic Bachelors", + "author": "Daniella Steel", + "genre": "Adult", + "read": false + }, + { + "id": 6, + "title": "Foxes in a fix", + "author": "Bruce Cameron", + "genre": "Animals", + "read": true + }, + { + "id": 7, + "title": "The Luzhin Defense", + "author": "Vladimir Nabokov", + "genre": "Chess", + "read": true + } +] \ No newline at end of file diff --git a/reading-list-manager/readingList.js b/reading-list-manager/readingList.js index 84febab..50707f5 100644 --- a/reading-list-manager/readingList.js +++ b/reading-list-manager/readingList.js @@ -1,53 +1,241 @@ // Place here the file operation functions for loading and saving books +const fs = require('fs'); +const chalk = require('chalk'); + +const FILE_NAME = 'books.json'; function loadBooks() { - // TODO: Implement this function - // Read from books.json - // Handle missing file (create empty array) - // Handle invalid JSON (notify user, use empty array) - // Use try-catch for error handling + try { + const content = fs.readFileSync('./books.json', 'utf8'); + const books = JSON.parse(content); + + if (!Array.isArray(books)) { + console.log(chalk.yellow('Books data was not an array – starting fresh')); + return []; + } + + return books; + } catch (error) { + if (error.code === 'ENOENT') { + console.log( + chalk.yellow('No books.json found. Starting with empty list.') + ); + return []; + } + if (error instanceof SyntaxError) { + console.log( + chalk.red( + 'Invalid JSON in books.json – file is broken. Starting fresh!' + ) + ); + return []; + } + console.log(chalk.red('Error loading books:'), error.message); + return []; + } } function saveBooks(books) { // TODO: Implement this function // Write books array to books.json // Use try-catch for error handling + try { + const data = JSON.stringify(books, null, 2); + fs.writeFileSync(FILE_NAME, data); + console.log(chalk.green('Saved :')); + } catch (error) { + console.log(chalk.red('Save failed'), error.message); + } } -function addBook(book) { +function addBook(books, titles, author, genre) { // TODO: Implement this function + //Create new book + const newBook = { + id: books.length + 1, + title: title, + author: author, + genre: genre, + read: false, + }; + + books.push(newBook); + + saveBooks(books); + + console.log(chalk.green(`✓ Added "${title}"`)); + + return books; } -function getUnreadBooks() { +function getUnreadBooks(books) { // TODO: Implement this function using filter() + const unreadBooks = books.filter(function (book) { + return book.read === false; + }); + + console.log(chalk.blue('\n--- UNREAD BOOKS ---')); + if (unreadBooks.length === 0) { + console.log(chalk.yellow('No unread books!')); + } else { + for (let i = 0; i < unreadBooks.length; i++) { + const book = unreadBooks[i]; + console.log(`${book.id}. ${book.title} by ${book.author}`); + } + } + + return unreadBooks; } -function getBooksByGenre(genre) { +function getBooksByGenre(books, genre) { // TODO: Implement this function using filter() + + console.log('DEBUG → books type:', typeof books); + console.log('DEBUG → is array?:', Array.isArray(books)); + console.log( + 'DEBUG → books length:', + books?.length ?? 'N/A (books not array)' + ); + + // Safe filter – prevents crash even if array has holes, undefined items, null, etc. + const genreBooks = books.filter( + (book) => + book && + typeof book === 'object' && // exists & is object (not null/undefined) + typeof book.genre === 'string' && // genre exists and is actually a string + book.genre.toLowerCase() === genre.toLowerCase() + ); + + console.log(chalk.blue(`\n--- ${genre} BOOKS ---`)); + + if (genreBooks.length === 0) { + console.log(chalk.yellow(`No ${genre} books!`)); + } else { + for (let i = 0; i < genreBooks.length; i++) { + const book = genreBooks[i]; + const readStatus = book.read ? '✓' : '○'; + console.log(`${readStatus} ${book.id}. ${book.title} by ${book.author}`); + } + } + + return genreBooks; } -function markAsRead(id) { +function markAsRead(books, id) { // TODO: Implement this function using map() + const updatedBooks = books.map(function (book) { + if (book.id === id) { + return { + id: book.id, + title: book.title, + author: book.author, + genre: book.genre, + read: true, + }; + } + + return book; + }); + + //Does book exist? + + const oldBook = books.find(function (book) { + return book.id === id; + }); + + if (!oldBook) { + console.log(chalk.red(`✗ Book #${id} not found!`)); + return books; + } + + // Save changes + saveBooks(updatedBooks); + console.log(chalk.green(`✓ Marked "${oldBook.title}" as read!`)); + + return updatedBooks; } -function getTotalBooks() { +function getTotalBooks(books) { // TODO: Implement this function using length + const total = books.length; + console.log(chalk.blue(`\nTotal books: ${total}`)); + return total; } -function hasUnreadBooks() { +function hasUnreadBooks(books) { // TODO: Implement this function using some() + const hasUnread = books.some(function (book) { + return book.read === false; + }); + + if (hasUnread) { + console.log(chalk.yellow('You have books to read!')); + } else { + console.log(chalk.green('All books are read!')); + } + + return hasUnread; } -function printAllBooks() { +function printAllBooks(books) { // TODO: Implement this function // Loop through and display with chalk // Use green for read books, yellow for unread // Use cyan for titles + console.log(chalk.blue('\n--- ALL BOOKS ---')); + + if (books.length === 0) { + console.log(chalk.yellow('No books yet!')); + return; + } + + for (let i = 0; i < books.length; i++) { + const book = books[i]; + let status; + if (book.read) { + status = chalk.green('✓ Read'); + } else { + status = chalk.red('○ Unread'); + } + console.log( + `${book.id}.${chalk.cyan(book.title)} by ${book.author} - ${status}` + ); + } } -function printSummary() { +function printSummary(books) { // TODO: Implement this function // Show statistics with chalk // Display total books, read count, unread count // Use bold for stats -} \ No newline at end of file + console.log(chalk.blue('\n--- READING SUMMARY ---')); + + // Total books + const total = books.length; + console.log(`Total: ${total}`); + + // Count read books + let readCount = 0; + for (let i = 0; i < books.length; i++) { + if (books[i].read) { + readCount++; + } + } + const unreadCount = total - readCount; + + console.log(chalk.green(`Read: ${readCount}`)); + console.log(chalk.red(`Unread: ${unreadCount}`)); +} + +module.exports = { + loadBooks, + saveBooks, + addBook, + getUnreadBooks, + getBooksByGenre, + markAsRead, + getTotalBooks, + hasUnreadBooks, + printAllBooks, + printSummary, +};