diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..af4eb91 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "semi": true, + "singleQuote": true, + "tabWidth": 2 +} + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..5707c1a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,105 @@ +{ + "name": "c55-core-week-6", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "c55-core-week-6", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "chalk": "^4.1.2" + }, + "devDependencies": { + "prettier": "^3.8.1" + } + }, + "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/prettier": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "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/package.json b/package.json new file mode 100644 index 0000000..b4337b2 --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "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/mareh-aboghanem/c55-core-week-6.git" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "module", + "bugs": { + "url": "https://github.com/mareh-aboghanem/c55-core-week-6/issues" + }, + "homepage": "https://github.com/mareh-aboghanem/c55-core-week-6#readme", + "dependencies": { + "chalk": "^4.1.2" + }, + "devDependencies": { + "prettier": "^3.8.1" + } +} diff --git a/reading-list-manager/app.js b/reading-list-manager/app.js index b0365ef..2830394 100644 --- a/reading-list-manager/app.js +++ b/reading-list-manager/app.js @@ -7,7 +7,26 @@ // 3. Show summary statistics // 4. Add example of filtering by genre or read/unread status // 5. Add example of marking a book as read - +import chalk from 'chalk'; +import { + printAllBooks, + printSummary, + getUnreadBooks, + getBooksByGenre, + markAsRead +} from './readingList.js'; console.log('šŸ“š MY READING LIST šŸ“š\n'); // Your implementation here +printAllBooks(); +printSummary(); + +console.log(chalk.bold('\nUnread Books:')); +const unread = getUnreadBooks(); +unread.forEach(book => { + console.log(`${book.id}. ${chalk.cyan(book.title)} by ${book.author} (${book.genre})`); +}); + +const exampleId = 1; +console.log(chalk.bold(`\nMarking book ${exampleId} as read...`)); +markAsRead(exampleId); \ No newline at end of file diff --git a/reading-list-manager/books.json b/reading-list-manager/books.json index 0637a08..169f5c2 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": "Atomic Habits", + "author": "James Clear", + "genre": "Self-Help", + "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/readingList.js b/reading-list-manager/readingList.js index 84febab..845831e 100644 --- a/reading-list-manager/readingList.js +++ b/reading-list-manager/readingList.js @@ -1,4 +1,12 @@ // Place here the file operation functions for loading and saving books +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import chalk from 'chalk'; +// to make sure we can read and write to books.json in the same directory as this file. +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const booksPath = path.join(__dirname, 'books.json'); function loadBooks() { // TODO: Implement this function @@ -6,48 +14,133 @@ function loadBooks() { // Handle missing file (create empty array) // Handle invalid JSON (notify user, use empty array) // Use try-catch for error handling + try{ + const readBooks = fs.readFileSync(booksPath, 'utf8'); + const books = JSON.parse(readBooks); + return Array.isArray(books) ? books : []; + } + catch (error) { + console.error('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 { + fs.writeFileSync(booksPath, JSON.stringify(books, null, 2), 'utf8');} + catch (error) { + /*This will occur when No write permission, Disk full, the folder is missing, File locked*/ + console.error('Error saving books:', error.message); + }} function addBook(book) { // TODO: Implement this function + // Check for duplicate title using some() + try{ + const books = loadBooks(); + const isDuplicate = books.some(existingBook => existingBook.title.toLowerCase() === book.title.toLowerCase()); + if (isDuplicate) { + throw new Error('Book with this title already exists'); + } + // Generate new ID + const lastBook = books[books.length - 1]; + const newId = lastBook ? lastBook.id + 1 : 1; + const newBook = { + id: newId, + title: book.title, + author: book.author, + genre: book.genre, + read: false + }; + books.push(newBook); + saveBooks(books); + console.log('Book added successfully!'); +} + catch (error) { + console.error('Error adding book:', error.message); + } } -function getUnreadBooks() { +export function getUnreadBooks() { // TODO: Implement this function using filter() + const books = loadBooks(); + return books.filter(book => !book.read); } -function getBooksByGenre(genre) { +export function getBooksByGenre(genre) { // TODO: Implement this function using filter() + const books = loadBooks(); + return books.filter(book => book.genre.toLowerCase() === genre.toLowerCase()); } -function markAsRead(id) { +export function markAsRead(id) { // TODO: Implement this function using map() + const books= loadBooks(); + const bookId = Number(id); + let found = false; + const statusofBooks = books.map(book => { + if (book.id === bookId) { + found = true; + return { ...book, read: true }; + } + return book; + }); + if (!found) { + console.log('No book found with this id.'); + return; + } + saveBooks(statusofBooks); } function getTotalBooks() { // TODO: Implement this function using length + const books = loadBooks(); + return books.length; } function hasUnreadBooks() { // TODO: Implement this function using some() + const books = loadBooks(); + return books.some(book => !book.read); } -function printAllBooks() { +export function printAllBooks() { // TODO: Implement this function // Loop through and display with chalk // Use green for read books, yellow for unread // Use cyan for titles + const books = loadBooks(); + if (books.length === 0) { + console.log(chalk.yellow('No books in the reading list.')); + return; + } + //console.log(chalk.bold('\nšŸ“š MY READING LIST šŸ“š\n')); + books.forEach(book => { + const status = book.read + ? chalk.green('āœ“ Read') + : chalk.yellow('⚠ Unread'); + console.log( + `${book.id}. ${chalk.cyan(book.title)} by ${book.author} (${book.genre}) ${status}` + ); + }); + } -function printSummary() { +export function printSummary() { // TODO: Implement this function // Show statistics with chalk // Display total books, read count, unread count // Use bold for stats + const books = loadBooks(); + const totalBooks = books.length; + const readBooks = books.filter(book => book.read).length; + const unreadBooks = books.filter(book => !book.read).length; + console.log(chalk.bold('\nšŸ“Š SUMMARY šŸ“Š\n')); + console.log(chalk.bold(`Total Books: ${totalBooks}`)); + console.log(chalk.bold(`Read: ${readBooks}`)); + console.log(chalk.bold(`Unread: ${unreadBooks}`)); } \ No newline at end of file