Skip to content
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,7 @@ dist
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

node_modules/
package-lock.json
.env
.DS_store
7 changes: 7 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 80
}
24 changes: 24 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
24 changes: 19 additions & 5 deletions reading-list-manager/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
52 changes: 51 additions & 1 deletion reading-list-manager/books.json
Original file line number Diff line number Diff line change
@@ -1 +1,51 @@
[]
[
{
"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
}
]
216 changes: 202 additions & 14 deletions reading-list-manager/readingList.js
Original file line number Diff line number Diff line change
@@ -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
}
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,
};