ToolDoBox
ToolDoBox Online
Unified productivity hub with projects, goals, journals, and realtime collaboration

What It Does


Why I Built This

I was juggling multiple productivity tools—separate apps for backlogs, journals, timers, and collaboration. Each had its own login, data silo, and UI patterns. The friction of context-switching was killing my flow. I wanted one cohesive, installable web app that could replace this fragmented workflow.

After retiring a legacy .NET API, I chose Supabase as the single backend. It gave me unified auth, storage, and realtime channels without maintaining a bespoke server. The challenge became building a modular frontend that could scale from personal use to small team collaboration while keeping data integrity and offline capabilities.


How It Works

The frontend is built with React 19, Vite 7, and TypeScript, styled with Tailwind utilities and CSS modules. Framer Motion powers the animations. Supabase handles PostgreSQL database, authentication, realtime channels, and Edge Functions on the backend.

A custom SupabaseGateway + BaseRepository + Command pattern encapsulates all data access, providing type safety, dependency injection, and consistent error handling. This architecture makes the codebase testable with Vitest unit tests and enables deterministic mocking for E2E scenarios.

Recent architectural improvements included a question identity migration that decouples template versions from user answers, ensuring historical journal entries never break when templates evolve. Row-level security policies protect data while still allowing public share links for the collaborative clipboard feature.


Impact

By the numbers:

What changed:


Challenges & Solutions

The biggest challenge was fixing goal persistence. Users were losing progress when using +/- controls because saves only triggered on input blur events. I had to centralize save logic and invoke saves on every interaction instead. This revealed that our E2E tests weren't matching real user behavior—synthetic blur events had been hiding the bug.

Another challenge was the question identity migration. Journal templates needed to version without breaking historical entries. I solved this by making identity_id stable across versions, decoupling read/write paths, and regenerating Supabase types. Then I added E2E coverage to guard against regressions.

Realtime collaboration introduced RLS policy complexity. Some policies were recursive and blocked public Clippy share links. I had to carefully refactor the policies to enforce ownership validation while still allowing public read access for shared items.


What I Learned

I learned that E2E test flows must align with real user behavior. Our tests used programmatic blur events that masked the persistence bug for months. Now I write tests that mimic actual click patterns and state transitions.

I also learned that centralized data layers (gateway + repositories) dramatically reduce drift in error handling and simplify test stubbing. The initial setup took time, but it paid dividends in code maintainability and test reliability.

Most importantly: selection-based or date-based "current entry" detection is safer than timestamp heuristics when dealing with concurrent data. Our original logic assumed the most recent entry by timestamp was always "current," but that broke when users had multiple goals active simultaneously.

Future improvements:


Links