Archived
feat: bookmarks and notes management
- Backend: Django REST Framework API with Bookmark and Note models - ViewSets with user-scoped querysets and select_related for N+1 prevention - Create/List/Detail/Update/Delete endpoints - Batch delete operations - Unique constraint on user+book+page for bookmarks - IsOwner permission class for object-level access control - Full serializer validation (page > 0, non-empty content, duplicate check) - 30+ pytest-django tests covering CRUD, auth, filtering, edge cases - Frontend: React TypeScript components - AnnotationsContext with useReducer for state management - BookmarkList, NoteList, AddAnnotationForm, AnnotationsDashboard - Inline note editing with immediate save - Batch delete support - API client with JWT auto-refresh interceptors - Paginated query hook for infinite scroll support - Responsive CSS with loading/empty states - Infrastructure: Django project with custom User model, JWT auth, CORS - PostgreSQL database models with proper FK and indexes - Django admin configuration for all models
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import { Layout } from "@/components/layout";
|
||||
import { BookmarksNotesPage } from "@/components/annotations/BookmarksNotesPage";
|
||||
import "./styles.css";
|
||||
|
||||
function App(): React.ReactElement {
|
||||
const path = window.location.pathname;
|
||||
|
||||
// Simple client-side routing
|
||||
if (path.startsWith("/books/") && path.includes("bookmarks-notes")) {
|
||||
// /books/:id/bookmarks-notes
|
||||
const bookId = path.split("/")[2];
|
||||
return (
|
||||
<Layout title="Cloud Reader">
|
||||
<BookmarksNotesPage bookId={bookId} />
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
if (path === "/bookmarks-notes" || path === "/bookmarks-notes/") {
|
||||
return (
|
||||
<Layout title="Cloud Reader">
|
||||
<BookmarksNotesPage />
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
// Default: landing page
|
||||
return (
|
||||
<Layout title="Cloud Reader">
|
||||
<div className="page">
|
||||
<h2>Welcome to Cloud Reader</h2>
|
||||
<p>Your personal e-book reader with cross-device sync.</p>
|
||||
<div className="quick-links">
|
||||
<a href="/bookmarks-notes" className="card-link">
|
||||
<h3>Bookmarks & Notes</h3>
|
||||
<p>View and manage all your annotations</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById("root");
|
||||
if (rootElement) {
|
||||
ReactDOM.createRoot(rootElement).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user