Commit Graph

169 Commits

Author SHA1 Message Date
soconnor 48d3335f12 fix: auth redir memoization 2026-06-11 12:09:22 -04:00
soconnor 1c24c90189 fix: build errors 2026-06-11 12:01:19 -04:00
Claude 6b1173b4db Switch from standalone to standard Next.js build
Removes output: "standalone" from next.config.js, updates build/start/preview
scripts to use next build/next start, and updates Dockerfile to copy .next
output directory and run via bun run start instead of server.js.

https://claude.ai/code/session_014126WHVRT8mftmqkU6dajG
2026-06-11 15:50:38 +00:00
Claude 17971f2a98 Disable React Compiler to fix SSR prerender on Linux
The React Compiler (reactCompiler: true) generates code that calls
react/compiler-runtime during SSR, which resolves to null on Linux/bun,
causing "Cannot read properties of null (reading 'useRef')" on every
statically prerendered page. Disabling restores normal SWC transforms.

https://claude.ai/code/session_014126WHVRT8mftmqkU6dajG
2026-06-11 15:47:48 +00:00
soconnor 7a6e255325 Merge pull request #2 from soconnor0919/claude/time-clock-invoice-g342lt
Fix Docker build: force-dynamic dashboard layout
2026-06-11 11:43:26 -04:00
Claude b10cf5dd2e Force dynamic rendering for all dashboard pages
Dashboard pages require authentication and include client components
with hooks that fail during static prerendering on Linux/Docker.
Setting force-dynamic in the layout prevents build-time prerendering
across the entire authenticated dashboard.

https://claude.ai/code/session_014126WHVRT8mftmqkU6dajG
2026-06-11 06:18:00 +00:00
soconnor a3d8120059 package updates 2026-06-11 01:49:06 -04:00
Claude 9c135d3289 Fix ESLint type import and add better-auth to serverExternalPackages
Prevents Turbopack from statically analyzing better-auth's kysely adapter,
which imports symbols removed in kysely 0.29. Also fixes @typescript-eslint
consistent-type-imports warning on the Db type alias.

https://claude.ai/code/session_014126WHVRT8mftmqkU6dajG
2026-06-11 05:45:55 +00:00
Claude e03c553b7f Add temporary PDF preview links via public token TTL
- Schema: add publicTokenExpiresAt to invoices table
- invoices.generatePublicToken: accepts optional ttlHours param; sets
  expiry timestamp when provided
- invoices.getByPublicToken: returns 403 if token is expired
- New route GET /api/i/[token]/pdf: streams the invoice PDF publicly,
  returns 410 Gone if expired; excluded from auth middleware
- MCP invoices_generate_public_token: exposes ttlHours, returns both
  webUrl (/i/{token}) and pdfUrl (/api/i/{token}/pdf)

https://claude.ai/code/session_014126WHVRT8mftmqkU6dajG
2026-06-11 05:37:17 +00:00
Claude c0a333710f Complete MCP coverage: 58 tools, invoice filters, templates, email config, profile
MCP expanded from 49 to 58 tools:
- templates_list, templates_list_by_type, templates_create, templates_update,
  templates_delete — full invoice template CRUD
- businesses_get_email_config, businesses_update_email_config — configure
  per-business Resend API key and sending domain
- profile_get, profile_update — user profile read/write

invoices_list now accepts optional status and clientId filters (e.g. list
all draft invoices, or all invoices for a specific client). Backed by a
new optional input on invoices.getAll in the tRPC router.

https://claude.ai/code/session_014126WHVRT8mftmqkU6dajG
2026-06-11 05:33:13 +00:00
Claude c6b6641dfa Expand MCP from 24 to 49 tools — full coverage of all major features
New tools added:

Expenses (5): expenses_list, expenses_get, expenses_create,
expenses_update, expenses_delete

Recurring invoices (7): recurring_list, recurring_create,
recurring_update, recurring_pause, recurring_resume,
recurring_generate_now, recurring_delete

Dashboard (1): dashboard_get_stats — revenue, pending, overdue,
client count, MoM change, 6-month chart, recent invoices

Invoice extras (8): invoices_get_current_open, invoices_send (email
with PDF attachment), invoices_send_reminder, invoices_generate_public_token,
invoices_revoke_public_token, invoices_bulk_update_status,
invoices_bulk_delete

New Zod schemas and JSON schemas for all added tools.

https://claude.ai/code/session_014126WHVRT8mftmqkU6dajG
2026-06-11 05:30:16 +00:00
Claude 868c34f005 Expose invoiceId in MCP time_clock_in tool
The backend now accepts invoiceId on clock-in to link a timer directly
to a specific invoice. Expose this in the MCP so AI clients can clock in
against a particular invoice rather than relying on latest-invoice lookup.
Also update time_clock_out description to reflect the new behavior.

https://claude.ai/code/session_014126WHVRT8mftmqkU6dajG
2026-06-11 05:11:54 +00:00
Claude feb8f36ce7 Integrate time clock directly into invoices, remove standalone page
- Remove "Time Clock" from sidebar navigation
- Redirect /dashboard/time-clock to /dashboard/invoices
- Add InvoiceTimerCard to invoice detail page (shown for non-paid invoices)
- Timer started from an invoice is explicitly linked to that invoice at clock-in
- On clock-out, time is added directly to the linked invoice as a line item
- Active timer widget on dashboard now shows which invoice is being tracked
- Backend: clockIn accepts invoiceId; clockOut prefers explicit invoiceId over
  searching for the latest draft invoice for the client

https://claude.ai/code/session_014126WHVRT8mftmqkU6dajG
2026-06-11 00:23:50 +00:00
soconnor 0d8efb0c5f chore: update dependencies (next, react, radix-ui, trpc, drizzle, etc.)
- next 16.2.4 → 16.2.7, react/react-dom 19.2.5 → 19.2.7
- @trpc/* 11.7 → 11.17, drizzle-orm 0.44 → 0.45, drizzle-kit 0.30 → 0.31
- tailwindcss/postcss 4.1 → 4.3, better-auth 1.4 → 1.6
- @tanstack/react-query 5.90 → 5.101, framer-motion, recharts, fuse.js
- All @radix-ui patch/minor bumps, prettier 3.6 → 3.8, typescript-eslint 8.60
- Skipped majors: zod, eslint, typescript, lucide-react, resend, react-day-picker

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 18:57:40 -04:00
soconnor 9762ede7ec feat: show live estimated hours/earnings while timer is running
Summary cards now include the in-progress session's hours and earnings,
rounded up to the nearest 15-min increment (matching clock-out billing
logic). A secondary "+Xh est." line appears below each stat when a timer
is active, updating every second as the elapsed counter ticks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 18:54:05 -04:00
soconnor 2b4608bfb4 feat: add missing MCP tools and fix undefined content response
- Add time_entries_update, time_entries_delete, time_entries_get_summary tools
- Fix textResult to use `data ?? null` so JSON.stringify always returns a
  string — procedures returning undefined (e.g. getRunning with no timer,
  businesses.getById not found) were dropping the text field from the MCP
  content item, causing client SDK validation failures

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 18:46:30 -04:00
soconnor c490556a02 fix: copy static assets into standalone dir after next build
next build with output:standalone does not automatically copy
.next/static or public into .next/standalone/. The standalone
server.js looks for them at __dirname/.next/static and
__dirname/public, so they must be copied there post-build.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 18:31:20 -04:00
soconnor 839016532c fix: move ssr:false dynamic imports into client component
Turbopack disallows ssr:false in Server Components. Extracted the three
recharts dynamic imports into charts-client.tsx ("use client"), which
page.tsx now imports from directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 18:26:42 -04:00
soconnor b1f0c98fdd fix: use node standalone server instead of next start
Dynamically import recharts components with ssr:false to prevent
server-side rendering of DOM-dependent charts, eliminating the
width/height -1 warnings and redacted SSR errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 18:24:59 -04:00
soconnor d2f39bfc35 refactor: replace custom migrate.ts with standard Drizzle migrator
The baseline/bogus-entry logic was a one-time workaround for a db:push
→ migration transition that is long past. It required a per-migration
isMigrationApplied case — missing cases caused today's invoiceId bug.
Now both db:migrate (dev) and Docker startup use the same standard
migrate() call. Drizzle's own tracking table handles idempotency.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 18:12:01 -04:00
soconnor 960f0c2d17 fix: add isMigrationApplied cases for migrations 0009–0012
Baseline logic was falling through to false for all migrations after
0008, causing incorrect behaviour on fresh deploys. Now correctly
detects api_keys table, time_entry table, invoiceId column, and
verification token value column type.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 18:07:35 -04:00
soconnor ef94b69e52 feat: show active timer on dashboard homepage with clock-out button
Adds a banner below the page header when a timer is running: shows
description, client, elapsed time (live), a Stop button that auto-links
to the latest invoice, and a Details link to the time clock page.
Query is prefetched server-side so the widget is instant on load.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 18:05:54 -04:00
soconnor 540150be33 fix: use node standalone server instead of next start
output: standalone doesn't support next start — use node .next/standalone/server.js

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 18:00:32 -04:00
soconnor d9e2bab779 feat: time clock links to latest client invoice + fix SSO verification token overflow
- Clock out and manual entry creation now auto-add a line item to the
  client's latest draft/sent invoice and return invoice info
- Time clock page shows invoice badge on each entry with a link
- Toast after clock-out/create includes "View Invoice" action when linked
- MCP time_clock_in now accepts optional startedAt for backdating
- MCP time_clock_out description updated to document invoice linking
- Migration 0012: widen beenvoice_verification_token.value to text to
  fix varchar(255) overflow during Authentik PKCE OAuth flow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 17:55:26 -04:00
soconnor 3c708b7914 time clock fixes 2026-06-06 17:18:32 -04:00
Claude b0e026c963 Fix ESLint errors: remove setState-in-effect, use nullish coalescing 2026-06-06 18:06:32 +00:00
Claude bdc25e0c58 Document Drizzle migration journal requirement in AGENTS.md 2026-06-06 18:03:55 +00:00
Claude cf7af6ae00 Add time_entries migration to Drizzle journal 2026-06-06 17:52:41 +00:00
Claude 29630ebc1f Add time clock feature with MCP access
- New beenvoice_time_entry DB table with migration (startedAt/endedAt, hours, rate, clientId)
- tRPC router with clockIn, clockOut, getRunning, getAll, getSummary, create, update, delete
- Dashboard page at /dashboard/time-clock with live elapsed timer, entry list, and manual entry form
- 5 MCP tools: time_clock_in, time_clock_out, time_get_running, time_entries_list, time_entries_create
- Sidebar navigation entry

Timer state is stored in PostgreSQL (endedAt IS NULL = running), suitable for serverless/Coolify deployment.
2026-06-06 17:05:17 +00:00
soconnor 413eb3e3c0 Allow MCP API route through proxy 2026-06-04 21:44:24 -04:00
soconnor bad24aeda4 Merge MCP API access 2026-06-04 21:40:08 -04:00
soconnor 37eb70be65 Add MCP API access 2026-06-04 21:33:32 -04:00
soconnor a13992e387 fix: use custom migrate.ts instead of drizzle-kit migrate in Docker
drizzle-kit migrate exits 0 even when migrations fail, so the server
would start on a broken schema. The custom migrate.ts uses drizzle-orm's
programmatic migrator and calls process.exit(1) on failure, which
prevents the server from starting and makes failures visible in Coolify.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 21:59:46 -04:00
soconnor f4c2435ddc fix: run db:migrate at container startup instead of build time
Migration now runs at container start (when DB is available) rather than
during the build stage (when no DB is reachable). The release image now
includes node_modules, drizzle.config.ts, and the drizzle/ folder so
drizzle-kit can apply pending migrations on each deploy.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 21:48:21 -04:00
soconnor 898422571f fix: remove hardcoded baseURL from auth client
NEXT_PUBLIC_ vars are baked in at build time, so NEXT_PUBLIC_APP_URL=localhost
was being embedded in the bundle. Removing baseURL lets better-auth use
window.location.origin automatically, which works correctly on any domain.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 21:41:05 -04:00
soconnor e3b2de5aa2 fix: replace invalid ADD CONSTRAINT IF NOT EXISTS with DO $$ BEGIN blocks
PostgreSQL does not support IF NOT EXISTS for ADD CONSTRAINT. Use PL/pgSQL
DO blocks to guard constraint creation idempotently instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 18:29:49 -04:00
soconnor c878a6ed69 chore: add isMigrationApplied check for migration 0008
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 18:17:38 -04:00
soconnor 95b5ba5171 chore: add migration 0008 for payments, recurring invoices, and public links
Adds invoice_payment, recurring_invoice, recurring_invoice_item tables and
publicToken/lastReminderSentAt/invoicePrefix columns to invoice. Uses
IF NOT EXISTS guards since invoicePrefix may have been applied via db:push.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 18:13:35 -04:00
soconnor f0c34160df feat: add recurring invoices, public links, time tracker, payments, reminders
- Recurring invoices: schedule-based auto-generation with CRUD UI at /dashboard/invoices/recurring and POST /api/cron/generate-recurring cron endpoint
- Public invoice link: generate/revoke shareable /i/[token] page for unauthenticated clients with PDF download
- Live time tracker: localStorage-persisted timer widget in invoice editor that appends a line item on stop (rounds to nearest 0.25h)
- Partial payment tracking: record payments per invoice, auto-mark paid when fully covered, balance due display
- Send reminder: email reminder via Resend with custom message dialog and last-sent indicator

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 17:17:58 -04:00
soconnor 47a0ccc88c fix: remove extra bottom padding from filter and pagination bars
CardContent defaults to px-5 pb-4. Tailwind's CSS cascade means pb-4
always wins over the py-0 override, leaving 16px of phantom bottom
padding on all thin toolbar cards. Replaced CardContent with plain divs
carrying the intended py-2 px-3 directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 03:10:37 -04:00
soconnor 7819e438df feat: add fuzzy autocomplete and NL quick-add for invoice line items
- Description field now shows a fuzzy-matched dropdown of past line items
  (description, hours, rate) as you type via Fuse.js — zero server cost
- Selecting a suggestion pre-fills description, hours, and rate in one click
- NL quick-add bar lets you type e.g. "3hrs web design @120" + Enter to
  append a fully-parsed line item without clicking through fields
- New tRPC query `getLineItemHistory` returns deduplicated past line items
  for the current user, ordered by recency
- New `parseLineItem` utility handles hours/rate extraction via regex

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 03:08:00 -04:00
soconnor 0ba9c2f029 refactor: simplify register page to single centered card
Remove two-column hero layout, matching the signin page style.
Compact centered card: logo, heading, name/email/password form, footer.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 02:35:33 -04:00
soconnor 02a951817c refactor: simplify signin page to single centered card
Remove two-column hero layout. Page is now a compact centered card:
logo, "Welcome back" heading, email/password form, and footer links.
Eliminates the left panel background mismatch issue.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 02:33:12 -04:00
soconnor 3983724866 fix: restore authentik trusted origin and remove auth card U-border shadow
- Auto-derive trusted origin from AUTHENTIK_ISSUER URL so OAuth callbacks
  are accepted without requiring a separate AUTHENTIK_ORIGIN env var
- Remove leftover ssoProvider schema mapping (no longer used with genericOAuth)
- Remove dead @better-auth/sso dependency from package.json
- Drop md:shadow-2xl/md:shadow-lg from auth cards — the downward box-shadow
  was rendering as a U-shaped border (bottom+sides, no top); border +
  backdrop-blur-xl provides sufficient visual separation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 02:24:46 -04:00
soconnor 0e46fdafb2 feat: add administration page and account role management
- Implemented `AdministrationContent` component for managing account roles.
- Created `AdministrationPage` to serve as the main entry point for administration tasks.
- Added PDF preview functionality with `PdfPreviewFrame` component for invoice generation.
- Introduced `InputColor` component for advanced color selection with various formats.
- Established color conversion utilities in `color-converter.ts` for handling color formats.
- Defined appearance-related schemas and types in `appearance.ts` for consistent theme management.
2026-04-30 10:50:50 -04:00
soconnor ddc2b42672 Refactor invoice data table and templates page for improved readability and functionality
- Cleaned up imports and formatted code for better readability in invoices-data-table.tsx.
- Enhanced invoice interface definitions for clarity.
- Improved toast messages for bulk delete and update actions.
- Refactored date formatting and status type retrieval for better readability.
- Simplified template management in templates page, extracting TemplateList component.
- Added registration toggle based on environment variable DISABLE_SIGNUPS.
- Updated navbar to conditionally render registration link based on allowRegistration prop.
- Enhanced error handling and validation in expenses and settings routers.
- Improved PDF export footer handling.
- Updated TRPC react integration for cleaner type imports.
2026-04-29 22:49:07 -04:00
soconnor dbb739b060 refactor: update SendEmailPage layout and remove SendEmailDialog component 2026-04-28 01:30:38 -04:00
soconnor bd3181fb9d feat: add PDF preview functionality and normalize email message handling 2026-04-28 01:26:47 -04:00
soconnor 915ec103fc feat: add email message field to invoices and update related components 2026-04-28 01:06:45 -04:00
soconnor 4108019eab feat: enhance PDF generation with improved line estimation and page budgeting 2026-04-28 00:44:00 -04:00