| Component | Status | Notes |
|---|---|---|
| UI/Frontend | ✅ Working | Dashboard, clients, invoices, time entries, settings |
| Demo Mode | ✅ Working | localStorage with fake data, good for testing |
| Detection Engine | ✅ Working | Real logic in calculations.ts and detectionEngine.ts |
| Database Schema | ✅ Ready | supabase/schema.sql - just needs to be deployed |
| Auth UI | ✅ Working | Login/signup pages exist |
| Stripe OAuth Flow | Connect flow exists, webhook handling partial | |
| Toggl Integration | API calls exist, sync logic incomplete |
| Component | Status | What's Missing |
|---|---|---|
| Supabase Connection | ❌ Not deployed | Need to run schema.sql in Supabase |
| Real User Data | ❌ | Only demo mode works, DataProvider needs testing |
| User Signup Flow | ❌ | Organization creation on signup not wired |
| Toggl Sync | ❌ | Fetches projects but doesn't import time entries |
| Stripe Payment Sync | ❌ | Webhook exists but doesn't update invoices |
| Email Notifications | ❌ | Not built |
| CSV Import | Parser exists, may have bugs |
- Go to supabase.com
- Create a new project (free tier is fine)
- Wait for it to initialize (~2 min)
- In Supabase, go to SQL Editor
- Copy entire contents of
supabase/schema.sql - Paste and click "Run"
- Should see "Success" for all statements
- Go to Settings → API
- Copy these values:
Project URL→ This is your SUPABASE_URLanon publickey → This is your SUPABASE_ANON_KEYservice_rolekey → This is your SUPABASE_SERVICE_KEY (keep secret!)
Create .env.local in project root:
# Supabase
NEXT_PUBLIC_SUPABASE_URL=https://xxxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGci...
SUPABASE_SERVICE_KEY=eyJhbGci...
# Stripe (optional for now)
STRIPE_SECRET_KEY=sk_test_...
STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
NEXT_PUBLIC_STRIPE_CLIENT_ID=ca_...
# App
NEXT_PUBLIC_APP_URL=http://localhost:3000npm run devGo to /login, try to sign up. If it creates a user in Supabase Auth, connection works.
The problem: When a user signs up, we need to:
- Create user in Supabase Auth ✅ (this works)
- Create an Organization for them ❌ (not happening)
- Create default FinancialSettings ❌ (trigger exists but org needs to exist first)
File: src/app/auth/callback/route.ts or create a signup handler
// After successful auth, create organization
const { data: user } = await supabase.auth.getUser();
if (user && user.user) {
// Check if org exists
const { data: existingOrg } = await supabase
.from('organizations')
.select('id')
.eq('owner_id', user.user.id)
.single();
if (!existingOrg) {
// Create organization
const { data: newOrg } = await supabase
.from('organizations')
.insert({
name: user.user.email?.split('@')[0] + "'s Agency",
owner_id: user.user.id,
})
.select()
.single();
// Settings created automatically by trigger
}
}The DataProvider.tsx already has Supabase fetch functions, but they need testing and debugging.
- fetchClients - Does it return data after you add a client via Supabase UI?
- fetchInvoices - Same test
- fetchTimeEntries - Same test
- saveClient - Does it actually insert into Supabase?
- Organization ID mismatch - Make sure queries filter by correct org_id
- RLS blocking queries - Check that user is authenticated
- Type mismatches - Supabase returns slightly different shapes
Currently data entry goes to localStorage. Need to make it save to Supabase.
| File | Current | Needed |
|---|---|---|
src/app/app/clients/new/page.tsx |
Calls dataStore.addClient() |
Call Supabase insert |
src/app/app/invoices/new/page.tsx |
Calls dataStore.addInvoice() |
Call Supabase insert |
src/app/app/time-entries/new/page.tsx |
Calls dataStore.addTimeEntry() |
Call Supabase insert |
// Instead of:
dataStore.addClient(clientData);
// Do:
const supabase = createBrowserClient(...);
const { data, error } = await supabase
.from('clients')
.insert({
...clientData,
organization_id: currentOrgId, // Get from context
})
.select()
.single();
if (error) {
// Handle error
} else {
// Success, redirect or update UI
}The detection engine (detectionEngine.ts) is actually the most complete part. It:
- Calculates unbilled hours
- Detects scope creep
- Finds overdue invoices
- Generates alerts with real $ amounts
- Add a client with
hour_limit: 10 - Add 15 hours of time entries for that client
- Run detection:
runDetectionEngine()should return a scope creep alert - Check the dashboard - should show the alert
Check:
- Is the client status 'active'?
- Are time entries in the current month?
- Is
settings.scope_creep_threshold_percentset correctly?
Current State: API connection exists, project list works
What's Missing:
- Fetch time entries from Toggl API
- Map Toggl projects to Retenu clients
- Insert time entries into database
- Avoid duplicates (check external_id)
File to modify: src/app/api/integrations/toggl/route.ts
Toggl API to call:
GET https://api.track.toggl.com/api/v9/me/time_entries
Current State: Webhook endpoint exists, receives events
What's Missing:
- When
invoice.paidevent comes in, find matching Retenu invoice - Update invoice status to 'paid'
- Set paid_date
File to modify: src/app/api/stripe/webhook/route.ts
Current State: Parser exists in upload page
What's Missing:
- Better error handling
- Client matching (map CSV client names to existing clients)
- Duplicate detection
File: src/app/app/time-entries/upload/page.tsx
To have something that actually works end-to-end:
[ ] Supabase deployed with schema
[ ] User can sign up and get an organization created
[ ] User can add clients (saved to Supabase)
[ ] User can add time entries (saved to Supabase)
[ ] User can add invoices (saved to Supabase)
[ ] Detection engine runs on real data
[ ] Alerts show on dashboard
[ ] User can mark alerts as resolved
That's your MVP. Everything else (Toggl, Stripe, emails) is enhancement.
# Check if Supabase is connected
npm run dev
# Then in browser console:
const { createBrowserClient } = await import('@supabase/ssr');
const supabase = createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
);
const { data, error } = await supabase.from('clients').select('*');
console.log(data, error);
# Check detection engine
import { runDetectionEngine } from '@/app/lib/detectionEngine';
const alerts = runDetectionEngine();
console.log(alerts);- Supabase setup (required for anything)
- Signup flow fix (can't use app without org)
- Data entry → Supabase (need real data)
- Test detection (core value prop)
- CSV import (easiest way to get lots of data)
- Toggl integration (nice to have)
- Stripe integration (nice to have)
- Email alerts (later)
| Task | Time |
|---|---|
| Supabase setup | 30 min |
| Fix signup flow | 1 hour |
| Data entry to Supabase | 2 hours |
| Test & debug detection | 2 hours |
| Fix CSV import | 1 hour |
| Total to MVP | ~7 hours |
After that, Toggl/Stripe are ~2-3 hours each.
src/app/auth/callback/route.ts # Fix signup flow
src/app/providers/DataProvider.tsx # Debug Supabase fetches
src/app/app/clients/new/page.tsx # Save to Supabase
src/app/app/invoices/new/page.tsx # Save to Supabase
src/app/app/time-entries/new/page.tsx # Save to Supabase
src/app/lib/detectionEngine.ts # Already works, just test
This is the honest path to a working product. No shortcuts, no fake it till you make it. Just wire up what's already built.