From 689eeda9e64d7f1ab4765eb7c582f64ac98e3257 Mon Sep 17 00:00:00 2001 From: Shadi Date: Tue, 17 Feb 2026 14:18:43 +0100 Subject: [PATCH 1/4] set up project --- .prettierignore | 3 ++ .prettierrc | 7 ++++ package-lock.json | 105 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 27 ++++++++++++ 4 files changed, 142 insertions(+) create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..63520da --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +node_modules +dist +build diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..b0ec5b3 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "es5", + "printWidth": 80 +} 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..9557366 --- /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", + "homepage": "https://github.com/shmoonwalker/c55-core-week-6#readme", + "bugs": { + "url": "https://github.com/shmoonwalker/c55-core-week-6/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/shmoonwalker/c55-core-week-6.git" + }, + "license": "ISC", + "author": "", + "type": "commonjs", + "main": "index.js", + "scripts": { + "format": "prettier --write ." + + }, + "dependencies": { + "chalk": "^4.1.2" + }, + "devDependencies": { + "prettier": "^3.8.1" + } +} From 38e7eeaaf02d6571a99808f67911a71d03c0e680 Mon Sep 17 00:00:00 2001 From: Shadi Date: Tue, 17 Feb 2026 15:28:40 +0100 Subject: [PATCH 2/4] Implement reading list functionality with book management features --- reading-list-manager/app.js | 36 +++++--- reading-list-manager/books.json | 45 +++++++++- reading-list-manager/readingList.js | 132 +++++++++++++++++++++------- 3 files changed, 168 insertions(+), 45 deletions(-) diff --git a/reading-list-manager/app.js b/reading-list-manager/app.js index b0365ef..d1fc888 100644 --- a/reading-list-manager/app.js +++ b/reading-list-manager/app.js @@ -1,13 +1,29 @@ -// This is the entrypoint for your application. -// 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 +import { + loadBooks, + addBook, + getUnreadBooks, + getBooksByGenre, + markAsRead, + printAllBooks, + printSummary +} from './readingList.js'; console.log('šŸ“š MY READING LIST šŸ“š\n'); -// Your implementation here +printAllBooks(); + +addBook({ title: 'The Alchemist', author: 'Paulo Coelho', genre: 'Fiction' }); + +printSummary(); + +console.log('\nšŸ” Fiction Books:'); +getBooksByGenre('Fiction').forEach(b => + console.log(` ${b.read ? 'āœ“' : '⚠'} ${b.title} by ${b.author}`) +); + +console.log('\nšŸ“Œ Unread Books:'); +getUnreadBooks().forEach(b => console.log(` - ${b.title} by ${b.author} (id: ${b.id})`)); + +markAsRead(3); + +printSummary(); diff --git a/reading-list-manager/books.json b/reading-list-manager/books.json index 0637a08..03ce85d 100644 --- a/reading-list-manager/books.json +++ b/reading-list-manager/books.json @@ -1 +1,44 @@ -[] \ No newline at end of file +[ + { + "id": 1, + "title": "1984", + "author": "George Orwell", + "genre": "Fiction", + "read": false + }, + { + "id": 2, + "title": "To Kill a Mockingbird", + "author": "Harper Lee", + "genre": "Fiction", + "read": true + }, + { + "id": 3, + "title": "The Great Gatsby", + "author": "F. Scott Fitzgerald", + "genre": "Classic", + "read": true + }, + { + "id": 4, + "title": "The Hobbit", + "author": "J.R.R. Tolkien", + "genre": "Fantasy", + "read": true + }, + { + "id": 5, + "title": "Clean Code", + "author": "Robert C. Martin", + "genre": "Programming", + "read": false + }, + { + "title": "The Alchemist", + "author": "Paulo Coelho", + "genre": "Fiction", + "id": 6, + "read": false + } +] \ No newline at end of file diff --git a/reading-list-manager/readingList.js b/reading-list-manager/readingList.js index 84febab..9373a84 100644 --- a/reading-list-manager/readingList.js +++ b/reading-list-manager/readingList.js @@ -1,53 +1,117 @@ -// Place here the file operation functions for loading and saving books +import fs from 'fs'; +import path from 'path'; +import chalk from 'chalk'; -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 +const { green, yellow, cyan, bold } = chalk; +const filePath = path.join(process.cwd(), 'books.json'); + + +export function loadBooks() { + try { + if (!fs.existsSync(filePath)) { + fs.writeFileSync(filePath, JSON.stringify([], null, 2)); + return []; + } + + const data = fs.readFileSync(filePath, 'utf-8'); + if (!data.trim()) return []; + + return JSON.parse(data); + } catch (err) { + console.error(chalk.red('⚠ Invalid books.json. Starting with empty array.')); + return []; + } } -function saveBooks(books) { - // TODO: Implement this function - // Write books array to books.json - // Use try-catch for error handling +export function saveBooks(books) { + try { + fs.writeFileSync(filePath, JSON.stringify(books, null, 2), 'utf-8'); + } catch (err) { + console.error(chalk.red('⚠ Error saving books:', err.message)); + } } -function addBook(book) { - // TODO: Implement this function + +export function addBook(book) { + if (!book.title || !book.author) { + console.error(chalk.red('⚠ Book must have title and author.')); + return; + } + + const books = loadBooks(); + const duplicate = books.find(b => b.title.toLowerCase() === book.title.toLowerCase()); + if (duplicate) { + console.error(chalk.red(`⚠ Book "${book.title}" already exists.`)); + return; + } + + book.id = books.length > 0 ? Math.max(...books.map(b => b.id)) + 1 : 1; + book.read = book.read ?? false; + books.push(book); + saveBooks(books); + console.log(green(`āœ” "${book.title}" added successfully!`)); } -function getUnreadBooks() { - // TODO: Implement this function using filter() +export function getUnreadBooks() { + return loadBooks().filter(b => !b.read); } -function getBooksByGenre(genre) { - // TODO: Implement this function using filter() +export function getBooksByGenre(genre) { + return loadBooks().filter(b => b.genre.toLowerCase() === genre.toLowerCase()); } -function markAsRead(id) { - // TODO: Implement this function using map() +export function markAsRead(id) { + const books = loadBooks(); + let found = false; + + const updated = books.map(b => { + if (b.id === id) { + found = true; + return { ...b, read: true }; + } + return b; + }); + + if (!found) { + console.error(chalk.red(`⚠ No book with id ${id}`)); + return; + } + + saveBooks(updated); + console.log(green(`āœ” Book id ${id} marked as read.`)); } -function getTotalBooks() { - // TODO: Implement this function using length +export function getTotalBooks() { + return loadBooks().length; } -function hasUnreadBooks() { - // TODO: Implement this function using some() +export function hasUnreadBooks() { + return loadBooks().some(b => !b.read); } -function printAllBooks() { - // TODO: Implement this function - // Loop through and display with chalk - // Use green for read books, yellow for unread - // Use cyan for titles + +export function printAllBooks() { + const books = loadBooks(); + if (!books.length) { + console.log(chalk.red('No books in library.')); + return; + } + + console.log(bold('\nšŸ“š All Books:\n')); + books.forEach(b => { + const status = b.read ? green('āœ“ Read') : yellow('⚠ Unread'); + console.log(`${cyan(b.title)} by ${b.author} (${b.genre}) ${status}`); + }); } -function printSummary() { - // 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 +export function printSummary() { + const books = loadBooks(); + const total = books.length; + const readCount = books.filter(b => b.read).length; + const unreadCount = books.filter(b => !b.read).length; + + console.log(bold('\nšŸ“Š SUMMARY šŸ“Š\n')); + console.log(bold(`Total Books: ${total}`)); + console.log(bold.green(`Read: ${readCount}`)); + console.log(bold.yellow(`Unread: ${unreadCount}`)); +} From 8f42557c1862681251e2aa2baad085ddfcf10435 Mon Sep 17 00:00:00 2001 From: Shadi Date: Tue, 17 Feb 2026 15:32:09 +0100 Subject: [PATCH 3/4] Implement reading list functionality with book management features --- .gitignore | 1 + package.json | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 2b76d7c..8a88bc3 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,7 @@ build/Release # Dependency directories node_modules/ +.env jspm_packages/ # Snowpack dependency directory (https://snowpack.dev/) diff --git a/package.json b/package.json index 9557366..c367d57 100644 --- a/package.json +++ b/package.json @@ -12,12 +12,15 @@ }, "license": "ISC", "author": "", - "type": "commonjs", - "main": "index.js", + "type": "module", + "main": "app.js", "scripts": { + + "start": "node app.js" + }, "format": "prettier --write ." - }, + , "dependencies": { "chalk": "^4.1.2" }, From d165a4eb8b08de1cb27929a4bc027f508ad8c3af Mon Sep 17 00:00:00 2001 From: Shadi Date: Tue, 17 Feb 2026 15:38:31 +0100 Subject: [PATCH 4/4] format using prettier --- package.json | 8 +++----- reading-list-manager/app.js | 8 +++++--- reading-list-manager/readingList.js | 29 ++++++++++++++++------------- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index c367d57..1faa805 100644 --- a/package.json +++ b/package.json @@ -15,12 +15,10 @@ "type": "module", "main": "app.js", "scripts": { - - "start": "node app.js" + "start": "node app.js", + "format": "prettier --write \"**/*.js\"" }, - "format": "prettier --write ." - - , + "dependencies": { "chalk": "^4.1.2" }, diff --git a/reading-list-manager/app.js b/reading-list-manager/app.js index d1fc888..a4c0a3f 100644 --- a/reading-list-manager/app.js +++ b/reading-list-manager/app.js @@ -5,7 +5,7 @@ import { getBooksByGenre, markAsRead, printAllBooks, - printSummary + printSummary, } from './readingList.js'; console.log('šŸ“š MY READING LIST šŸ“š\n'); @@ -17,12 +17,14 @@ addBook({ title: 'The Alchemist', author: 'Paulo Coelho', genre: 'Fiction' }); printSummary(); console.log('\nšŸ” Fiction Books:'); -getBooksByGenre('Fiction').forEach(b => +getBooksByGenre('Fiction').forEach((b) => console.log(` ${b.read ? 'āœ“' : '⚠'} ${b.title} by ${b.author}`) ); console.log('\nšŸ“Œ Unread Books:'); -getUnreadBooks().forEach(b => console.log(` - ${b.title} by ${b.author} (id: ${b.id})`)); +getUnreadBooks().forEach((b) => + console.log(` - ${b.title} by ${b.author} (id: ${b.id})`) +); markAsRead(3); diff --git a/reading-list-manager/readingList.js b/reading-list-manager/readingList.js index 9373a84..8319d60 100644 --- a/reading-list-manager/readingList.js +++ b/reading-list-manager/readingList.js @@ -5,7 +5,6 @@ import chalk from 'chalk'; const { green, yellow, cyan, bold } = chalk; const filePath = path.join(process.cwd(), 'books.json'); - export function loadBooks() { try { if (!fs.existsSync(filePath)) { @@ -18,7 +17,9 @@ export function loadBooks() { return JSON.parse(data); } catch (err) { - console.error(chalk.red('⚠ Invalid books.json. Starting with empty array.')); + console.error( + chalk.red('⚠ Invalid books.json. Starting with empty array.') + ); return []; } } @@ -31,7 +32,6 @@ export function saveBooks(books) { } } - export function addBook(book) { if (!book.title || !book.author) { console.error(chalk.red('⚠ Book must have title and author.')); @@ -39,13 +39,15 @@ export function addBook(book) { } const books = loadBooks(); - const duplicate = books.find(b => b.title.toLowerCase() === book.title.toLowerCase()); + const duplicate = books.find( + (b) => b.title.toLowerCase() === book.title.toLowerCase() + ); if (duplicate) { console.error(chalk.red(`⚠ Book "${book.title}" already exists.`)); return; } - book.id = books.length > 0 ? Math.max(...books.map(b => b.id)) + 1 : 1; + book.id = books.length > 0 ? Math.max(...books.map((b) => b.id)) + 1 : 1; book.read = book.read ?? false; books.push(book); saveBooks(books); @@ -53,18 +55,20 @@ export function addBook(book) { } export function getUnreadBooks() { - return loadBooks().filter(b => !b.read); + return loadBooks().filter((b) => !b.read); } export function getBooksByGenre(genre) { - return loadBooks().filter(b => b.genre.toLowerCase() === genre.toLowerCase()); + return loadBooks().filter( + (b) => b.genre.toLowerCase() === genre.toLowerCase() + ); } export function markAsRead(id) { const books = loadBooks(); let found = false; - const updated = books.map(b => { + const updated = books.map((b) => { if (b.id === id) { found = true; return { ...b, read: true }; @@ -86,10 +90,9 @@ export function getTotalBooks() { } export function hasUnreadBooks() { - return loadBooks().some(b => !b.read); + return loadBooks().some((b) => !b.read); } - export function printAllBooks() { const books = loadBooks(); if (!books.length) { @@ -98,7 +101,7 @@ export function printAllBooks() { } console.log(bold('\nšŸ“š All Books:\n')); - books.forEach(b => { + books.forEach((b) => { const status = b.read ? green('āœ“ Read') : yellow('⚠ Unread'); console.log(`${cyan(b.title)} by ${b.author} (${b.genre}) ${status}`); }); @@ -107,8 +110,8 @@ export function printAllBooks() { export function printSummary() { const books = loadBooks(); const total = books.length; - const readCount = books.filter(b => b.read).length; - const unreadCount = books.filter(b => !b.read).length; + const readCount = books.filter((b) => b.read).length; + const unreadCount = books.filter((b) => !b.read).length; console.log(bold('\nšŸ“Š SUMMARY šŸ“Š\n')); console.log(bold(`Total Books: ${total}`));