Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { BrowserRouter, Routes, Route } from "react-router";
import AppShell from "./components/AppShell/AppShell";
import { BrowsePage } from "./pages/BrowsePage";

/* ---------- Temporary placeholder pages (delete when the real ones exist) ---------- */
function BrowsePlaceholder() {
return <h1>Browse page</h1>;
}
function ShelfPlaceholder() {
return <h1>My shelf page</h1>;
}
Expand All @@ -18,7 +16,7 @@ export default function App() {
<BrowserRouter>
<Routes>
<Route element={<AppShell />}>
<Route index element={<BrowsePlaceholder />} />
<Route index element={<BrowsePage />} />
<Route path="shelf" element={<ShelfPlaceholder />} />
<Route path="book/:id" element={<DetailPlaceholder />} />
</Route>
Expand Down
11 changes: 11 additions & 0 deletions client/src/data/mockBooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type {Book} from '../types/book';
import booksJson from '../../../server/test-data/books.json';

export type ShelfEntry = {bookId: Book['id']; rating?: number};

export const mockBooks: Book[] = booksJson.map(({googleBooksId, ...rest}) => ({
id: googleBooksId,
...rest,
}));

export const mockShelfEntries: ShelfEntry[] = [];
46 changes: 46 additions & 0 deletions client/src/pages/BrowsePage.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*mobile first */
/* TODO: replace the raw values with the shared design tokens in src/styles/. */

.page {
max-width: 1200px;
margin: 0 auto;
padding: 1.5rem 1rem;
}

.header {
display: flex;
flex-wrap: wrap;
align-items: baseline;
justify-content: space-between;
gap: 0.25rem 1rem;
margin-bottom: 1.25rem;
}

.heading {
margin: 0;
}

.count {
margin: 0;
opacity: 0.75;
}

.grid {
list-style: none;
margin: 0;
padding: 0;
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 1.5rem 1rem;
}

@media (min-width: 40rem) { /*tablets and bigger devices*/
.page {
padding: 2rem 1.5rem;
}

.grid {
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
gap: 2rem 1.5rem;
}
}
49 changes: 49 additions & 0 deletions client/src/pages/BrowsePage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {useState} from 'react';
import type {Book} from '../types/book';
import {BookCard} from '../components/BookCard/BookCard';
import {mockBooks, mockShelfEntries, type ShelfEntry} from '../data/mockBooks';
import styles from './BrowsePage.module.css';

export function BrowsePage(){
const [shelf, setShelf] = useState<Map<Book['id'], ShelfEntry>>( () => new Map(mockShelfEntries.map((entry) => [entry.bookId, entry]))
);

function toggleSaved(bookId: Book['id']) {
setShelf((prev) => {
const next = new Map(prev);
if (next.has(bookId)) {
next.delete(bookId);
} else {
next.set(bookId, {bookId});
}
return next;
});
}

const books = mockBooks;

return (
<section className = {styles.page} aria-labelledby = "browse-heading">
<header className = {styles.header}>
<h1 id = "browse-heading" className = {styles.heading}>
browse
</h1>
<p className = {styles.count}> {books.length} books</p>
</header>
<ul className = {styles.grid}>
{books.map((book) => {
const entry = shelf.get(book.id);
return (
<BookCard
key = {book.id}
book = {book}
isSaved = {entry !== undefined}
myRating = {entry?.rating}
onToggleSaved = {() => toggleSaved(book.id)}
/>
);
})}
</ul>
</section>
);
}