Frontend

React-based frontend application for managing notes and receipts with PocketBase authentication.

Architecture

The frontend uses a generic resource management pattern to eliminate code duplication between similar resources:

Core Components

  • useResourceManager.tsx - Generic hook for CRUD operations and state management
  • ResourceManager.tsx - Generic UI component for resource management pages
  • resource-configs.tsx - Resource-specific configurations

Resource-Specific Components

  • NotesManager.tsx - Notes management interface (uses generic components)
  • ReceiptsManager.tsx - Receipts management interface (uses generic components)
  • NoteForm.tsx / ReceiptForm.tsx - Resource-specific forms
  • NoteList.tsx / ReceiptList.tsx - Resource-specific lists
  • NoteItem.tsx / ReceiptItem.tsx - Individual item components

Shared Components

  • App.tsx - Main application component with authentication state management
  • HomePage.tsx - Landing page with navigation
  • LoginForm.tsx - User login form
  • useGeolocation.tsx - Geolocation hook for receipts

Features

  • User authentication with email/password
  • Generic CRUD operations for multiple resource types
  • Real-time UI updates
  • Responsive design with TailwindCSS
  • Local storage for auth token persistence
  • Error handling and loading states
  • Geolocation support for receipts
  • Star/unstar functionality for receipts

Authentication Flow

  1. User enters email/password in LoginForm
  2. App stores auth token in localStorage
  3. Token is included in all API requests via Authorization header
  4. App checks token validity on page load
  5. User can logout to clear token and return to login

Adding New Resources

To add a new resource type (e.g., tasks, bookmarks):

  1. Define the TypeScript types in shared/types.ts
  2. Create a resource configuration in resource-configs.tsx
  3. Create resource-specific Form and List components
  4. Create a Manager component using the generic ResourceManager

Example:

// In resource-configs.tsx export const tasksResourceConfig: ResourceManagerConfig<Task, CreateTaskRequest, UpdateTaskRequest> = { apiPath: "/api/tasks", resourceName: "task", resourceNamePlural: "tasks", mapCreateRequest: (data) => ({ title: data.title }), mapUpdateRequest: (data) => data, updateItemInList: (items, id, updatedItem) => items.map((item) => item.id === id ? updatedItem : item), }; // In TasksManager.tsx export default function TasksManager({ user, onLogout }) { const resourceManager = useResourceManager(tasksResourceConfig); return ( <ResourceManager {...resourceManager} user={user} onLogout={onLogout} title="Tasks Manager" FormComponent={TaskForm} ListComponent={TaskList} // ... other props /> ); }

Benefits of Generic Architecture

  • Eliminated ~200 lines of duplicate code between Notes and Receipts managers
  • Type-safe - Full TypeScript support with generics
  • Consistent UX - All resources follow the same interaction patterns
  • Easy to extend - Adding new resources requires minimal boilerplate
  • Maintainable - Bug fixes and improvements apply to all resources