From e33d684c3f59ab85c474d380e6e913515d1fe1f7 Mon Sep 17 00:00:00 2001 From: "dagimgit config --global user.email dagimvanhaileselassie@gmail.com" Date: Wed, 18 Feb 2026 23:54:42 +0100 Subject: [PATCH] feat: complete reading-list-manager assignment with full functionality --- reading-list-manager/.gitignore | 1 + reading-list-manager/.prettierrc | 4 ++ reading-list-manager/app.js | 16 ++++- reading-list-manager/books.json | 38 +++++++++++- reading-list-manager/package-lock.json | 86 ++++++++++++++++++++++++++ reading-list-manager/package.json | 16 +++++ reading-list-manager/readingList.js | 74 +++++++++++++++++++++- 7 files changed, 230 insertions(+), 5 deletions(-) create mode 100644 reading-list-manager/.gitignore create mode 100644 reading-list-manager/.prettierrc create mode 100644 reading-list-manager/package-lock.json create mode 100644 reading-list-manager/package.json diff --git a/reading-list-manager/.gitignore b/reading-list-manager/.gitignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/reading-list-manager/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/reading-list-manager/.prettierrc b/reading-list-manager/.prettierrc new file mode 100644 index 0000000..650cb88 --- /dev/null +++ b/reading-list-manager/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "semi": true +} diff --git a/reading-list-manager/app.js b/reading-list-manager/app.js index b0365ef..6752788 100644 --- a/reading-list-manager/app.js +++ b/reading-list-manager/app.js @@ -1,5 +1,13 @@ // This is the entrypoint for your application. // node app.js +import { + loadBooks, + printAllBooks, + printSummary, + getBooksByGenre, + getUnreadBooks, + markAsRead, +} from './readingList.js'; // TODO: Implement the main application logic here // 1. Load books on startup @@ -7,7 +15,11 @@ // 3. Show summary statistics // 4. Add example of filtering by genre or read/unread status // 5. Add example of marking a book as read - console.log('šŸ“š MY READING LIST šŸ“š\n'); - // Your implementation here +const books = loadBooks(); +printAllBooks(); +printSummary(); +console.log('\nFiction Books:', getBooksByGenre('Fiction')); +console.log('\nUnread Books:', getUnreadBooks()); +markAsRead(1); diff --git a/reading-list-manager/books.json b/reading-list-manager/books.json index 0637a08..a640f05 100644 --- a/reading-list-manager/books.json +++ b/reading-list-manager/books.json @@ -1 +1,37 @@ -[] \ No newline at end of file +[ + { + "id": 1, + "title": "1984", + "author": "George Orwell", + "genre": "Fiction", + "read": true + }, + { + "id": 2, + "title": "Dune", + "author": "Frank Herbert", + "genre": "Sci-Fi", + "read": false + }, + { + "id": 3, + "title": "The Hobbit", + "author": "J.R.R. Tolkien", + "genre": "Fantasy", + "read": true + }, + { + "id": 4, + "title": "Sapiens", + "author": "Yuval Noah Harari", + "genre": "Non-fiction", + "read": false + }, + { + "id": 5, + "title": "Clean Code", + "author": "Robert C. Martin", + "genre": "Programming", + "read": true + } +] \ No newline at end of file diff --git a/reading-list-manager/package-lock.json b/reading-list-manager/package-lock.json new file mode 100644 index 0000000..fd54748 --- /dev/null +++ b/reading-list-manager/package-lock.json @@ -0,0 +1,86 @@ +{ + "name": "reading-list-manager", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "reading-list-manager", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "chalk": "^4.1.2" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + } + } +} diff --git a/reading-list-manager/package.json b/reading-list-manager/package.json new file mode 100644 index 0000000..386a5e6 --- /dev/null +++ b/reading-list-manager/package.json @@ -0,0 +1,16 @@ +{ + "name": "reading-list-manager", + "version": "1.0.0", + "description": "", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "module", + "dependencies": { + "chalk": "^4.1.2" + } +} diff --git a/reading-list-manager/readingList.js b/reading-list-manager/readingList.js index 84febab..9238cf0 100644 --- a/reading-list-manager/readingList.js +++ b/reading-list-manager/readingList.js @@ -1,4 +1,6 @@ // Place here the file operation functions for loading and saving books +import fs from 'node:fs'; +import chalk from 'chalk'; function loadBooks() { // TODO: Implement this function @@ -6,36 +8,72 @@ function loadBooks() { // Handle missing file (create empty array) // Handle invalid JSON (notify user, use empty array) // Use try-catch for error handling -} + try { + if (!fs.existsSync('books.json')) { + return []; + } + const data = fs.readFileSync('books.json', 'utf8'); + if (!data.trim()) { + return []; + } + return JSON.parse(data); + } catch (error) { + console.log(chalk.red('Error loading books.json - using empty list')); + return []; + } +} function saveBooks(books) { // TODO: Implement this function // Write books array to books.json // Use try-catch for error handling + try { + fs.writeFileSync('books.json', JSON.stringify(books, null, 2), 'utf8'); + } catch (error) { + console.log(chalk.red('Error saving books.json')); + } } function addBook(book) { // TODO: Implement this function + const books = loadBooks(); + const nextId = books.length ? Math.max(...books.map((b) => b.id)) + 1 : 1; + const newBook = { id: nextId, read: false, ...book }; + books.push(newBook); + saveBooks(books); + console.log(chalk.green('Added book:'), chalk.cyan(newBook.title)); } function getUnreadBooks() { // TODO: Implement this function using filter() + return loadBooks().filter((book) => !book.read); } function getBooksByGenre(genre) { // TODO: Implement this function using filter() + return loadBooks().filter( + (book) => book.genre.toLowerCase() === genre.toLowerCase(), + ); } function markAsRead(id) { // TODO: Implement this function using map() + const books = loadBooks(); + const updated = books.map((book) => + book.id === id ? { ...book, read: true } : book, + ); + saveBooks(updated); + console.log(chalk.green(`Book ${id} marked as read`)); } function getTotalBooks() { // TODO: Implement this function using length + return loadBooks().length; } function hasUnreadBooks() { // TODO: Implement this function using some() + return loadBooks().some((book) => !book.read); } function printAllBooks() { @@ -43,6 +81,17 @@ function printAllBooks() { // Loop through and display with chalk // Use green for read books, yellow for unread // Use cyan for titles + const books = loadBooks(); + console.log(chalk.bold('\nšŸ“š MY READING LIST šŸ“š\n')); + console.log('All Books:'); + + books.forEach((book, index) => { + const status = book.read ? chalk.green('āœ“ Read') : chalk.yellow('⚠ Unread'); + + console.log( + `${index + 1}. ${chalk.cyan(book.title)} by ${book.author} (${book.genre}) ${status}`, + ); + }); } function printSummary() { @@ -50,4 +99,25 @@ function printSummary() { // Show statistics with chalk // Display total books, read count, unread count // Use bold for stats -} \ No newline at end of file + const books = loadBooks(); + const total = books.length; + const readCount = books.filter((b) => b.read).length; + const unreadCount = total - readCount; + + console.log(chalk.bold('\nšŸ“Š SUMMARY šŸ“Š')); + console.log(chalk.bold('Total Books:'), total); + console.log(chalk.bold('Read:'), readCount); + console.log(chalk.bold('Unread:'), unreadCount); +} +export { + loadBooks, + saveBooks, + addBook, + getUnreadBooks, + getBooksByGenre, + markAsRead, + getTotalBooks, + hasUnreadBooks, + printAllBooks, + printSummary, +};