Skip to content
Draft
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
1 change: 1 addition & 0 deletions .Jules/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
## [Unreleased]

### Added
- **Error Boundary System**: Implemented `ErrorBoundary` component to catch runtime errors and provide a "Reload" option, preventing white-screen crashes (`web/components/ErrorBoundary.tsx`, `web/App.tsx`).
- Inline form validation in Auth page with real-time feedback and proper ARIA accessibility support (`aria-invalid`, `aria-describedby`, `role="alert"`).
- Dashboard skeleton loading state (`DashboardSkeleton`) to improve perceived performance during data fetch.
- Comprehensive `EmptyState` component for Groups and Friends pages to better guide new users.
Expand Down
86 changes: 40 additions & 46 deletions .Jules/knowledge.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,31 @@ colors: {

## Component Patterns

### Error Boundary Pattern

**Date:** 2026-01-13
**Context:** Implementing global error handling

React Error Boundaries must be class components. To use hooks (like `useTheme` or `useNavigate`) in the fallback UI, render a separate functional component inside the `render` method:

```tsx
class ErrorBoundary extends Component {
// ... lifecycle methods
render() {
if (this.state.hasError) {
return <ErrorFallback error={this.state.error} />;
}
return this.props.children;
}
}

// Functional component can use hooks
const ErrorFallback = () => {
const { style } = useTheme();
return <div className={style === 'neo' ? '...' : '...'}>...</div>;
}
```

### Button Component Variants

**Date:** 2026-01-01
Expand Down Expand Up @@ -291,6 +316,7 @@ When writing Playwright scripts to verify frontend changes without backend:
1. **Auth Mocking:** You must mock `/users/me` persistently. If this call fails or returns 401, `AuthContext` will force a redirect to login, breaking navigation tests.
2. **Route Matching:** Use specific route patterns (e.g., `**/users/me`) and ensure they don't accidentally swallow longer paths (like `**/users/me/balance-summary`) if using wildcards carelessly. Register specific paths before general ones if using `page.route` order dependence, or use specific globs.
3. **Response Structure:** Mocks must match the structure expected by `axios` interceptors and components. If `axios` returns `res.data` as the body, and the component expects `res.data.groups`, the mock body should be `{"groups": [...]}` (not `{"data": {"groups": ...}}`).
4. **Strict Mode & Text Locators:** When using `get_by_text()`, be aware that Playwright's strict mode will fail if multiple elements match (e.g., a chart label and a summary card). Use `.first` or a more specific locator chain to resolve ambiguity.

---

Expand Down Expand Up @@ -406,59 +432,27 @@ Tailwind breakpoints used:

---

## Project Direction & Goals

### What Splitwiser Is About
## Recent Implementation Reviews

**Date:** 2026-01-01
### ✅ Successful PR Pattern: Error Boundary (#237)

Splitwiser is focused on (per README):
1. **Modern expense splitting** - Making group expenses effortless
2. **Group management** - Create and manage expense groups
3. **Real-time synchronization** - Keep data synced across devices
4. **Secure authentication** - JWT-based auth with refresh tokens
5. **Receipt management** - Track and store receipt images
6. **Debt simplification** - Minimize number of transactions
7. **Multi-currency support** - Handle different currencies
8. **Exceptional UX** - Beautiful, intuitive, delightful interactions
9. **Cross-platform** - Web (React/Vite/TypeScript) and mobile (Expo/React Native)

**Current Implementation Details:**
- Web app uses dual-theme design system (Glassmorphism & Neobrutalism)
- Mobile uses React Native Paper (Material Design)
- Backend: FastAPI + MongoDB

**NOT focused on:**
- Generic business app features
- Traditional accounting workflows
- Enterprise features
- One-off utilities

**When picking tasks, ask:**
- Does this improve expense splitting experience?
- For web: Does it work in BOTH themes?
- Will users notice and appreciate this?
- Does it align with the README's core features?
**Date:** 2026-01-13
**Context:** Review of merged async agent PRs

---
**What was implemented:**
1. Created `ErrorBoundary` class component.
2. Created `ErrorFallback` functional component using existing `Card` and `Button` UI.
3. Integrated `ErrorBoundary` into `App.tsx`, wrapping `AppRoutes` and `ToastContainer`.
4. Validated with simulated crash.

_Document errors and their solutions here as you encounter them._

```markdown
<!-- Template for documenting errors:
### Error: [Error Name]
**Date:** YYYY-MM-DD
**Context:** What you were trying to do
**Error Message:** The actual error
**Solution:** How you fixed it
**Files Affected:** List of files
-->
```
**Why it succeeded:**
- ✅ Correct use of Class component for Error Boundary.
- ✅ Correct separation of Logic (Class) and UI (Functional/Hooks).
- ✅ Reused existing accessible UI components.
- ✅ Supported both themes (Neo/Glass) via `Card`.

---

## Recent Implementation Reviews

### ✅ Successful PR Pattern: Toast Notification System (#227)

**Date:** 2026-01-13
Expand Down
5 changes: 3 additions & 2 deletions .Jules/todo.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@
- Impact: Guides new users, makes app feel polished
- Size: ~70 lines

- [ ] **[ux]** Error boundary with retry for API failures
- Files: Create `web/components/ErrorBoundary.tsx`, wrap app
- [x] **[ux]** Error boundary with retry for API failures
- Completed: 2026-01-13
- Files: `web/components/ErrorBoundary.tsx`, `web/App.tsx`
- Context: Catch errors gracefully with retry button
- Impact: App doesn't crash, users can recover
- Size: ~60 lines
Expand Down
3 changes: 3 additions & 0 deletions web/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { AuthProvider, useAuth } from './contexts/AuthContext';
import { ThemeProvider } from './contexts/ThemeContext';
import { ToastProvider } from './contexts/ToastContext';
import { ToastContainer } from './components/ui/Toast';
import { ErrorBoundary } from './components/ErrorBoundary';
import { Auth } from './pages/Auth';
import { Dashboard } from './pages/Dashboard';
import { Friends } from './pages/Friends';
Expand Down Expand Up @@ -51,8 +52,10 @@ const App = () => {
<ToastProvider>
<AuthProvider>
<HashRouter>
<ErrorBoundary>
<AppRoutes />
<ToastContainer />
</ErrorBoundary>
</HashRouter>
</AuthProvider>
</ToastProvider>
Expand Down
70 changes: 70 additions & 0 deletions web/components/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React, { Component, ErrorInfo, ReactNode } from 'react';
import { Card } from './ui/Card';
import { Button } from './ui/Button';
import { AlertTriangle, RefreshCw } from 'lucide-react';

interface Props {
children: ReactNode;
}

interface State {
hasError: boolean;
error: Error | null;
}

export class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { hasError: false, error: null };
}

static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}

componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error('ErrorBoundary caught an error:', error, errorInfo);
}

handleRetry = () => {
this.setState({ hasError: false, error: null });
window.location.reload();
};

render() {
if (this.state.hasError) {
return (
<div className="h-screen w-full flex items-center justify-center p-4">
<ErrorFallback error={this.state.error} onRetry={this.handleRetry} />
</div>
);
}

return this.props.children;
}
}

const ErrorFallback = ({ error, onRetry }: { error: Error | null, onRetry: () => void }) => {
return (
<Card className="max-w-md w-full text-center" title="Something went wrong">
<div className="flex flex-col items-center gap-4 py-4">
<div className="p-3 rounded-full bg-red-100 text-red-600 dark:bg-red-900/30 dark:text-red-400">
<AlertTriangle size={32} />
</div>

<p className="text-gray-600 dark:text-gray-300">
{error?.message || 'An unexpected error occurred while rendering this page.'}
</p>

<Button
variant="primary"
onClick={onRetry}
className="w-full flex items-center justify-center gap-2"
>
<RefreshCw size={18} />
Reload Page
</Button>
</div>
</Card>
);
};
Loading