Book Notes to aplikacja internetowa stworzona z myślą o miłośnikach książek, którzy chcą w jednym miejscu przechowywać informacje o przeczytanych pozycjach, dodawać do nich własne notatki oraz recenzje. Aplikacja umożliwia łatwe zarządzanie kolekcją przeczytanych książek, ich edycję oraz sortowanie według różnych kryteriów.

Kluczowe Funkcjonalności:
Formatowanie Daty: Daty przeczytania książek są automatycznie formatowane dla lepszej czytelności.
Wyświetlanie Listy Książek: Główna strona aplikacji dynamicznie pobiera i prezentuje listę wszystkich dodanych książek.
Dodawanie Notatek i Opisów: Użytkownik może do każdej książki dodać szczegółowy opis oraz własne przemyślenia czy ważne cytaty w sekcji notatek.
Edycja Danych: Istnieje możliwość edycji opisu oraz notatek dla każdej książki. Zmiany są zapisywane bezpośrednio w bazie danych.
Usuwanie Książek: Użytkownik może usunąć wybrane pozycje ze swojej kolekcji.
Sortowanie: Aplikacja pozwala na sortowanie zgromadzonych książek.


Aspekty Techniczne i Działanie:
Aplikacja została zbudowana w oparciu o nowoczesne technologie webowe, co zapewnia jej wydajność i skalowalność.
Backend (Node.js z Express.js):
- Serwer aplikacji oparty jest na środowisku Node.js oraz frameworku Express.js.
- Połączenie z bazą danych PostgreSQL: Aplikacja łączy się z bazą danych
book-notesprzy użyciu bibliotekipg.
// index.js
import pg from "pg";
const db = new pg.Client({
user: "postgres",
host: "localhost",
database: "book-notes",
password: "qwerty123", // Przykładowe hasło, powinno być zabezpieczone
port: 5432,
});
db.connect();- Wyświetlanie książek (Endpoint
/): Po wejściu na stronę główną, aplikacja pobiera wszystkie książki z tabelibooksi przekazuje je do szablonu EJS.
// index.js
app.get("/", async (req, res) => {
try {
const dbRes = await db.query("SELECT * FROM books");
let books = dbRes.rows;
books.forEach((book) => {
let date = getFormattedDate(book.readdate); // Formatowanie daty
book.date = date;
});
res.render("index.ejs", {
books: books
});
} catch (err) {
console.error("Error executing query", err.stack);
}
});- Formatowanie daty: Używana jest pomocnicza funkcja do formatowania daty.
// index.js
function getFormattedDate(date) {
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
return `<span class="math-inline">\{year\}\-</span>{month}-${day}`;
}- Sortowanie książek (Endpoint
/sort): Użytkownik może wybrać kryterium sortowania, a aplikacja dynamicznie odpytuje bazę danych z odpowiednią klauzuląORDER BY.
// index.js
app.post("/sort", async (req, res) => {
try {
let sortType = req.body.sortType;
let dbRes;
if(sortType === "title"){
dbRes = await db.query("SELECT * FROM books ORDER BY title ASC");
} else if (sortType === "newest") {
dbRes = await db.query("SELECT * FROM books ORDER BY readdate DESC");
} else if (sortType === "best") {
dbRes = await db.query("SELECT * FROM books ORDER BY recomendationratio DESC");
}
// ... reszta kodu renderująca widok ...
let books = dbRes.rows;
books.forEach((book) => {
let date = getFormattedDate(book.readdate);
book.date = date;
});
res.render("index.ejs", {
books: books
});
} catch (err) {
console.error("Error executing query", err.stack);
}
});- Edycja szczegółów książki (Endpoint
/editDetails): Po przesłaniu formularza edycji, dane książki (opis i notatki) są aktualizowane w bazie.
// index.js
app.post("/editDetails", async (req, res) => {
const updatedId = req.body.updatedBookId;
const updatedDescription = req.body.updatedBookDescription.trim();
const updatedNotes = req.body.updatedBookNotes.trim();
try {
await db.query(
"UPDATE books SET description = $1, notes = $2 WHERE id = $3",
[updatedDescription, updatedNotes, updatedId]
);
res.redirect("/"); // Przekierowanie na stronę główną
} catch (err) {
console.error("Error updating book details.", err.stack);
}
});- Usuwanie książki (Endpoint
/delete): Wybrana książka jest usuwana z bazy danych na podstawie jej ID.
// index.js
app.post("/delete", async (req, res) => {
const deletedItemId = req.body.deletedBookId;
try {
await db.query("DELETE FROM books WHERE id = $1", [deletedItemId]);
res.redirect("/"); // Przekierowanie na stronę główną
} catch (err) {
console.log(err);
}
});- Middleware: Wykorzystywany jest
body-parserdo przetwarzania danych z formularzy orazexpress.staticdo serwowania plików statycznych (np. CSS).
// index.js
import bodyParser from "body-parser";
app.use(express.static("public"));
app.use(bodyParser.urlencoded({extended: true}));- EJS (Embedded JavaScript templating): Strony HTML są dynamicznie generowane. Serwer przekazuje dane do szablonów
.ejs, które następnie renderują finalny kod HTML. Przykładem jest przekazywanie listy książek doindex.ejs. - HTML5 i CSS3: Struktura (HTML) i wygląd (CSS) aplikacji są zdefiniowane w standardowy sposób. Plik
public/styles/main.csszawiera reguły stylów. Przykładowy fragment CSS dla pojedynczej książki:
/* public/styles/main.css */
.book {
display: flex;
gap: 20px;
border: 1px solid #000;
padding: 15px;
margin-bottom: 20px;
background-color: #f9f9f9;
align-items: flex-start;
}
.bookDetails {
display: grid;
grid-template-rows: 187px auto auto auto;
grid-template-columns: 125px minmax(125px, 675px);
gap: 10px;
}Frontend:
Środowisko Uruchomieniowe i Narzędzia:
- Nodemon: Używany do automatycznego restartu serwera przy zmianach w kodzie.
- npm: Służy do zarządzania zależnościami projektu.
Jak to Działa (Podsumowanie Przepływu):
- Uruchomienie serwera za pomocą
nodemon index.js. - Użytkownik otwiera stronę główną (
/). - Express.js obsługuje żądanie, pobiera dane książek z PostgreSQL.
- Dane są przekazywane do szablonu
index.ejs, który generuje HTML. - Interakcje użytkownika (sortowanie, edycja, usuwanie) wysyłają żądania POST.
- Serwer przetwarza żądanie, modyfikuje dane w bazie (jeśli to konieczne) i odświeża widok lub przekierowuje.
Projekt “Book Notes” stanowi praktyczne zastosowanie popularnych technologii backendowych i frontendowych do stworzenia użytecznej aplikacji do zarządzania osobistą kolekcją książek.