Analysis and roadmap for transforming Retenu into a production-ready SaaS
Retenu is a Retenu detection tool for agencies that tracks clients, time entries, and invoices to identify underbilling, scope creep, missing invoices, and margin issues. The core detection engine and UI foundation are solid, but significant work remains for production readiness.
File: src/app/(auth)/login/page.tsx
The login page is a static mock that doesn't actually authenticate:
- Form inputs are uncontrolled (no state binding)
- "Sign In" button is just a Link to
/app(bypasses auth) - No connection to Supabase auth
- No error handling or loading states
Fix: Wire up the AuthProvider and make form functional with proper validation.
Files: supabase/schema.sql, supabase/seed.sql
The seed.sql references tables/columns that don't exist in schema.sql:
userstable referenced in seed but not in schemarevenue_alertstable in seed vsalertstable in schemacontractstable missingorganization_idNOT NULL in some INSERT statements
Fix: Align seed.sql with schema.sql or update schema to match.
File: src/app/app/clients/[id]/page.tsx
The client detail page directly imports dataStore and getClientStats from detectionEngine - it doesn't use the DataProvider context. This means:
- Supabase mode is ignored
- Realtime updates don't work
- Data is stale
Fix: Migrate to use useData() hook like the dashboard.
File: src/app/app/alerts/page.tsx
Same issue as client detail - directly uses dataStore and runDetectionEngine() instead of DataProvider.
Fix: Migrate to use useData() hook.
File: src/lib/actions.ts
Multiple lines have // @ts-ignore comments inline with code (syntax error from bad merge):
const { data } = await supabase// @ts-ignoreorganizations')Fix: Properly format the .from('tablename') calls.
There's no UI to create an organization after signup. Users authenticate but have no org, so all queries fail silently.
Fix: Add onboarding flow that creates organization + financial_settings after first login.
File: src/app/components/Sidebar.tsx
The sidebar has hidden md:flex - completely hidden on mobile with no hamburger menu or mobile nav.
Fix: Add mobile navigation drawer/sheet.
Files: src/app/api/integrations/toggl/route.ts, src/app/api/integrations/clockify/route.ts
The sync endpoints exist but:
- No automatic/scheduled sync (requires manual trigger)
- No deduplication of existing entries
- No handling of deleted entries in source system
- Project-to-client mapping is stored but never used during sync
Fix:
- Store mapping persistently in integrations.config
- Apply mapping during sync
- Add upsert logic based on external_id
- Consider cron job for background sync
Stripe webhook receives invoice events, but:
- No initial sync of historical invoices
- No mapping of Stripe customers to Retenu clients
- Webhook doesn't check if invoice already exists
Fix: Add Stripe invoice list sync endpoint and customer-to-client mapping.
Alerts are calculated on-the-fly by detectionEngine but:
- No "Resolve" or "Ignore" buttons in UI
- No persistence of resolved/ignored status in local mode
- Server actions exist but aren't connected to UI
Fix: Add alert action buttons and wire to server actions (Supabase) or dataStore (local).
File: src/app/app/time-entries/upload/page.tsx
The file exists but I didn't read it. Likely needs:
- Column mapping UI
- Validation feedback
- Error handling for malformed data
Status: Needs review and completion.
File: src/app/app/settings/page.tsx
Financial settings UI exists but:
- Only shows "link to integrations"
- No actual settings form to edit thresholds, rates, currency
- No connection to
updateFinancialSettingsserver action
Fix: Build out settings form with all FinancialSettings fields.
Invoice list exists, but:
- No edit page like clients have
- No delete confirmation
- No mark-as-paid quick action in list view
Fix: Add invoice CRUD operations in UI.
Time entries can be added but not edited. Need:
- Edit modal or page
- Inline editing in table
- Bulk delete with confirmation
Several API routes use service role key without verifying the requesting user:
src/app/api/integrations/route.ts- Anyone with org_id can fetch integrations- Toggl/Clockify sync endpoints same issue
Fix: Add middleware to verify session and ownership.
No validation that required env vars are set. App will crash cryptically if:
- NEXT_PUBLIC_SUPABASE_URL is missing
- STRIPE_SECRET_KEY is missing during Stripe operations
Fix: Add startup validation with clear error messages.
No rate limiting on:
- Auth endpoints
- API routes
- Webhook endpoints
Fix: Add rate limiting middleware (Upstash ratelimit or similar).
Stripe webhook verifies signature, but:
- Error handling exposes internals
- No replay protection (timestamp check)
Review: Ensure production hardening of webhook handler.
Console.error statements may log sensitive data:
console.error('Error fetching integrations:', error);Fix: Sanitize error logging in production.
Some pages have loading spinners, others don't. Need:
- Consistent loading skeleton components
- Suspense boundaries for streaming
No error boundaries - entire app crashes on component error.
Fix: Add error.tsx files for graceful degradation.
EmptyStates.tsx exists but:
- Not all pages use appropriate empty states
- Demo mode banner could be more contextual
Client forms have basic required validation but:
- No inline validation feedback
- No server error display
- Currency inputs should use proper formatting
Fix: Add react-hook-form or similar with zod validation.
- Forms missing proper labels in some places
- No focus management after navigation
- Charts may not be screen-reader friendly
- Color contrast not audited
Fix: Run axe audit and address issues.
- Dashboard cards could stack better on mobile
- Tables need horizontal scroll or card view on mobile
- Chart tooltips may be cut off on small screens
Currently single-user per org. Need:
team_memberstable with roles- Invite flow
- Permission checks in UI and API
currency field exists in settings but:
- All calculations assume USD
- No exchange rate handling
- Formatters hardcoded to USD
- Month-over-month comparison
- Leakage trends over time
- Client profitability trends
- Export to CSV/PDF
- Alert digests
- Payment reminders
- Weekly summary reports
Suggested: Use Resend or SendGrid with Supabase Edge Functions.
For users who want custom automation beyond Toggl/Clockify/Stripe.
Make the app installable with:
- manifest.json
- Service worker
- Offline support for viewing data
src/app/lib/types.tsandsrc/lib/database.types.tshave overlapping definitions- Should generate types from Supabase schema
Fix: Use supabase gen types typescript and derive app types from DB types.
The localStorage-based dataStore works but:
- No reactive updates when data changes
- Singleton pattern doesn't work well with React
- Should be replaced with zustand or jotai for local-first mode
Demo clients have hardcoded 2026 dates. Should be relative to current date.
No tests exist. Need:
- Unit tests for calculations.ts
- Integration tests for API routes
- E2E tests for critical flows (login, add client, view alerts)
| Priority | Item | Effort | Impact |
|---|---|---|---|
| P0 | Fix login page | 2h | Blocking |
| P0 | Fix schema/seed mismatch | 1h | Blocking |
| P0 | Migrate pages to DataProvider | 4h | Blocking |
| P1 | Add onboarding flow | 4h | High |
| P1 | Mobile navigation | 2h | High |
| P1 | Fix actions.ts syntax | 1h | High |
| P2 | Complete time entry sync | 8h | Medium |
| P2 | Invoice sync & mapping | 8h | Medium |
| P2 | Settings page form | 4h | Medium |
| P2 | Alert resolution UI | 4h | Medium |
| P3 | API authorization | 4h | Medium |
| P3 | Error boundaries | 2h | Medium |
| P3 | Form validation | 6h | Medium |
| P3 | Invoice CRUD | 4h | Low |
| P4 | Test coverage | 16h | Low |
| P4 | Team members | 16h | Low |
| P4 | Email notifications | 8h | Low |
- Fix login page - Users can't actually log in
- Fix schema mismatch - Database setup will fail
- Migrate remaining pages to DataProvider - Supabase mode is broken
- Add onboarding flow - First-run experience is broken
- Add mobile nav - App unusable on phones
- Keep the dual local/Supabase mode - it's great for demos
- Use react-query or SWR for data fetching instead of raw useEffect
- Add proper form library (react-hook-form + zod)
- Consider tRPC for type-safe API routes
- Move to app router middleware for auth checks
- Use Supabase Realtime more extensively
- Consider multi-tenant architecture if scaling beyond single-org
- Evaluate local-first sync solutions (PowerSync, ElectricSQL) for offline support
- Consider separate API service if complexity grows
Last updated: 2026-03-07