clockIn and create used `input.clientId?.trim() ?? null`, but clients
always send `""` (not undefined) for no-client. `??` doesn't catch the
empty string, so `""` was inserted into time_entries.client_id,
violating the foreign key to clients.id. Use `|| null` to normalize a
blank string to null, matching updateRunning.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CRON_SECRET was required (min 32 chars) in production by env.js, but
neither docker-compose.yml nor docker-compose.coolify.yml passed it
through. Since the release container isn't covered by
SKIP_ENV_VALIDATION (build-stage only), bun run start failed env
validation on every docker deploy unless CRON_SECRET was manually
configured — even though it only gates the optional recurring-invoice
cron endpoint, which already handles being unset gracefully.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Updated README and documentation to reflect the transition from MinIO to Garage for S3-compatible storage.
- Removed the MinIO-specific docker-compose files and replaced them with Garage configurations.
- Adjusted environment variables and service dependencies in docker-compose files to accommodate Garage.
- Modified application code to log hints for Garage instead of MinIO.
- Ensured all references to MinIO in the codebase and documentation are replaced with Garage.
Link clocked time to invoice items with bidirectional sync, add entry editing on web, broaden session cookie detection for Expo clients, and handle API rate limits without signing users out.
Co-authored-by: Cursor <cursoragent@cursor.com>
Show a disabled-registration state on the register page when DISABLE_SIGNUPS is true (default), document docker-deploy.sh with git-SHA image tags, and align onboarding progress circles and labels on a shared grid.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move invoice import configuration into settings, redesign the import flow with shared components and sample templates, document the demo account in README, and polish upload and button styling.
Co-authored-by: Cursor <cursoragent@cursor.com>
Drop file fetches and embedded fonts that fail during static OG generation; use default ImageResponse typography instead.
Co-authored-by: Cursor <cursoragent@cursor.com>
Official URL migration preserves sessions, shortcuts prefs, and last clock-in client; auth screens match web with legal links; time clock and invoice editor/send flows are updated for the new domain and UI patterns.
Co-authored-by: Cursor <cursoragent@cursor.com>
Refresh onboarding wizard and shell, tighten invoice edit/detail flows, align timer widgets with the redesigned clock panel, and assign admin role on first signup.
Co-authored-by: Cursor <cursoragent@cursor.com>
Shared layout, tabs, and sidebar timer; user onboarding and registration polish; settings danger zone and data export; chart and tRPC perf fixes; migrations for onboarding and dropped appearance columns.
Co-authored-by: Cursor <cursoragent@cursor.com>
Give the landing, legal, and sign-in flows a consistent product shell while keeping marketing pages free of tRPC/session calls, fixing dev auth URL handling, and refreshing env and deploy docs.
Co-authored-by: Cursor <cursoragent@cursor.com>
Use FormData and server-side sign-in after register, trust proxy origins for reverse-proxy deploys, pass public URL build args in Docker, and clarify that git pull plus up -d --build is required to ship code and run pending migrations.
Co-authored-by: Cursor <cursoragent@cursor.com>
Route web registration through authClient.signUp.email, set explicit better-auth baseURL/secret for Docker, and harden the mobile register API with safeParse and readable validation messages.
Co-authored-by: Cursor <cursoragent@cursor.com>
Parse DISABLE_SIGNUPS and related env booleans correctly for Compose string values, and derive auth trustedOrigins from BETTER_AUTH_URL. Rewrite README and architecture docs with the git.soconnor.dev remote and accurate deployment guidance. Allow zero-line-item draft invoices with validation when sending email.
Co-authored-by: Cursor <cursoragent@cursor.com>
Fixes App Store Connect warnings when the widget extension CFBundle version
does not match the containing application.
Co-authored-by: Cursor <cursoragent@cursor.com>
Combine clients and businesses under entities, polish the web time clock,
and show live invoice PDF preview with tighter line-item editing.
Co-authored-by: Cursor <cursoragent@cursor.com>
Enable App Store builds without EAS, iOS 18 App Intents plugins, and signing
fixes for distribution export. Add mobile invoice PDF preview, compact line
items, and more reliable shortcut deep-link handling.
Co-authored-by: Cursor <cursoragent@cursor.com>
0015 was already marked applied with the wrong column name, so redeploys
skipped it; 0016 drops send_reminder_at and adds sendReminderAt.
Co-authored-by: Cursor <cursoragent@cursor.com>
Use camelCase sendReminderAt to match existing invoice columns; the
snake_case name caused dashboard queries to fail after deploy.
Co-authored-by: Cursor <cursoragent@cursor.com>
Restrict line item edits to draft invoices, auto-create drafts on clock-out,
and add sendReminderAt scheduling with dashboard due reminders.
Co-authored-by: Cursor <cursoragent@cursor.com>
Privacy Policy and Terms now share a document layout with table of contents,
plain-paragraph copy, and beenvoice.soconnor.dev contact details for App Store review.
Co-authored-by: Cursor <cursoragent@cursor.com>
Mobile app detects SSO per server, supports OAuth sign-in, and preserves saved
sessions when adding accounts. Tab screens get proper chrome layout and tab-bar
clearance with scrollable page headers.
Co-authored-by: Cursor <cursoragent@cursor.com>
Returns whether Authentik and signups are enabled so the app can show the right
sign-in options per instance at runtime.
Co-authored-by: Cursor <cursoragent@cursor.com>
Extract privacy and terms content into reusable components, replace auth modals with links to /privacy and /terms, add settings legal section, and remove duplicate legal-modal markup.
Co-authored-by: Cursor <cursoragent@cursor.com>
Flatten widget layouts and use system colors so banner and expanded regions render on vibrant lock screens; migrate auth sessions per account to prevent double sign-in; scope app lock PIN to accounts; default clock description to "Clock In"; add architecture docs and deferred form validation on auth screens.
Co-authored-by: Cursor <cursoragent@cursor.com>
Register the app with EAS, including the Live Activity widget extension and biometric permissions needed for production iOS builds.
Co-authored-by: Cursor <cursoragent@cursor.com>
Use brand mark and wordmark images in the time clock Live Activity, migrate file copies to the modern expo-file-system File API, and add eas.json for TestFlight production builds.
Co-authored-by: Cursor <cursoragent@cursor.com>
Default to beenvoice.soconnor.dev with server settings hidden behind Advanced; add Entities tab with clients/businesses, invoice creation, UI fixes for dashboard layout, date fields, FAB position, and card-matched button radius.
Co-authored-by: Cursor <cursoragent@cursor.com>
Expo app with dashboard, time clock, invoices, and settings — native tabs, glass UI, theme-aware components, and iOS Live Activities.
Co-authored-by: Cursor <cursoragent@cursor.com>
Enable session-based auth from the Expo companion app via SecureStore cookies and trusted deep-link origins.
Co-authored-by: Cursor <cursoragent@cursor.com>
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>
- 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>
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>
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>
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>
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>
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>