Initialize Beenvoice workspace

This commit is contained in:
2026-08-16 21:42:57 -04:00
commit 068a51b46b
5 changed files with 202 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
{
"version": "0.0.1",
"configurations": [
{
"name": "beenvoice-web-dev",
"runtimeExecutable": "bash",
"runtimeArgs": ["-c", "cd beenvoice-web && PORT=3010 bun run dev"],
"port": 3010
}
]
}
+19
View File
@@ -0,0 +1,19 @@
node_modules/
.turbo/
.next/
.expo/
dist/
coverage/
.env
.env.*
!.env.example
!.env.*.example
.ios-release.env
ios/
android/
*.tsbuildinfo
*.log
.DS_Store
+95
View File
@@ -0,0 +1,95 @@
# beenvoice workspace
Local development layout for the **beenvoice** product: a freelancer/small-business invoicing platform with a Next.js web app and an Expo mobile companion.
```
beenvoice-meta/
├── beenvoice-web/ # Web API + dashboard (Next.js 16, tRPC, PostgreSQL)
├── beenvoice-app/ # Mobile app (Expo, React Native, dev client)
└── beenvoice.icon/ # iOS app icon asset (icon composer)
```
Each subdirectory is its own git repository. This folder is a convenience layout for working on both clients against one API.
| Repo | Remote |
|------|--------|
| Web | [git.soconnor.dev/soconnor/beenvoice-web](https://git.soconnor.dev/soconnor/beenvoice-web) |
| Mobile | [git.soconnor.dev/soconnor/beenvoice-app](https://git.soconnor.dev/soconnor/beenvoice-app) |
## Quick start (full stack)
```bash
# 1. API + database
cd beenvoice-web
cp .env.example .env.local # edit DATABASE_URL, AUTH_SECRET, etc.
docker compose -f docker-compose.dev.yml up -d
bun install && bun run db:push
bun run dev # http://localhost:3000
# 2. Mobile (simulator)
cd ../beenvoice-app
cp .env.example .env # EXPO_PUBLIC_API_URL=http://localhost:3000
bun install
bun run ios # Metro on :8082, native dev build
```
Physical iPhone: set `EXPO_PUBLIC_API_URL` to your Mac's LAN IP; ensure `beenvoice-web` has `BETTER_AUTH_URL` matching a host the device can reach (trusted origins are derived from `BETTER_AUTH_URL` / `NEXT_PUBLIC_APP_URL`).
## Production deploy (Docker)
From `beenvoice-web` with `.env` configured (see [beenvoice-web/README.md](./beenvoice-web/README.md#docker-deployment-app--database)):
```bash
cd beenvoice-web
git pull
./scripts/docker-deploy.sh # rebuild app image + restart (not docker-compose.dev.yml)
```
Plain `docker compose up -d` without `--build` reuses the existing local image and will not pick up code from `git pull`.
## How the pieces connect
| Layer | Repo | Transport | Auth |
|-------|------|-----------|------|
| Web UI | `beenvoice-web` | tRPC `/api/trpc` (cookies) | better-auth session |
| Mobile UI | `beenvoice-app` | tRPC `/api/trpc` (cookie header) | better-auth + `@better-auth/expo` → SecureStore |
| Automation | `beenvoice-web` MCP `/api/mcp` | JSON-RPC | API key (`bv_…`) only |
| Public invoices | `beenvoice-web` `/i/[token]` | HTTP | unauthenticated token |
Mobile imports **tRPC router types** from `beenvoice-web/src/server/api/root` via `tsconfig` path mapping — keep API changes type-checked in both repos.
## Documentation map
| Doc | Contents |
|-----|----------|
| [beenvoice-web/README.md](./beenvoice-web/README.md) | Web setup, Docker deploy, scripts |
| [beenvoice-web/docs/ARCHITECTURE.md](./beenvoice-web/docs/ARCHITECTURE.md) | Server architecture (dense) |
| [beenvoice-app/README.md](./beenvoice-app/README.md) | Mobile setup, troubleshooting |
| [beenvoice-app/docs/ARCHITECTURE.md](./beenvoice-app/docs/ARCHITECTURE.md) | Mobile architecture (dense) |
| [beenvoice-web/docs/](./beenvoice-web/docs/) | UI guides (forms, tables, breadcrumbs, email) |
## Shared domain concepts
- **Clients** — billable contacts with optional default hourly rate
- **Businesses** — sender profiles (logo, address, Resend email config)
- **Invoices** — draft → sent → paid; line items; PDF + email; public share token
- **Time entries** — clock in/out; one running timer per user; auto-attach to open invoice
- **Recurring invoices**, **expenses**, **payments**, **templates** — full web support; mobile covers a core CRUD subset
## Environment cheat sheet
| Variable | Where | Purpose |
|----------|-------|---------|
| `DATABASE_URL` | beenvoice-web | PostgreSQL |
| `AUTH_SECRET`, `BETTER_AUTH_URL` | beenvoice-web | better-auth |
| `EXPO_PUBLIC_API_URL` | beenvoice-app | API base for mobile |
| `DISABLE_SIGNUPS` | beenvoice-web | `true` blocks new registrations |
| `RESEND_*` | beenvoice-web | Invoice / reset email |
## Ports
| Service | Default port |
|---------|----------------|
| Next.js (beenvoice-web) | 3000 |
| Postgres (dev compose) | 5432 |
| Metro (beenvoice-app) | **8082** (intentionally not 8081) |
+11
View File
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 252 436" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1,0,0,1,-1363.75,-282.196)">
<g transform="matrix(1,0,0,1,1343.05,673.674)">
<g transform="matrix(488.128,0,0,488.128,0,0)">
<path d="M0.262,0.09L0.262,-0.802L0.341,-0.802L0.341,0.09L0.262,0.09ZM0.307,0.012C0.255,0.012 0.21,0.002 0.171,-0.018C0.133,-0.038 0.103,-0.066 0.081,-0.103C0.059,-0.14 0.046,-0.184 0.042,-0.236L0.164,-0.243C0.169,-0.21 0.177,-0.183 0.19,-0.162C0.202,-0.14 0.219,-0.123 0.239,-0.112C0.259,-0.101 0.283,-0.096 0.311,-0.096C0.34,-0.096 0.364,-0.099 0.383,-0.106C0.402,-0.113 0.416,-0.123 0.425,-0.136C0.435,-0.149 0.44,-0.165 0.44,-0.184C0.44,-0.204 0.435,-0.221 0.426,-0.236C0.417,-0.25 0.4,-0.262 0.375,-0.274C0.349,-0.285 0.313,-0.297 0.265,-0.308C0.219,-0.32 0.181,-0.334 0.15,-0.352C0.12,-0.369 0.097,-0.391 0.082,-0.417C0.067,-0.443 0.059,-0.474 0.059,-0.51C0.059,-0.551 0.068,-0.587 0.087,-0.617C0.106,-0.647 0.133,-0.671 0.169,-0.687C0.205,-0.704 0.248,-0.712 0.299,-0.712C0.349,-0.712 0.392,-0.703 0.427,-0.685C0.463,-0.667 0.491,-0.641 0.511,-0.607C0.531,-0.573 0.544,-0.533 0.548,-0.486L0.426,-0.48C0.422,-0.506 0.416,-0.528 0.406,-0.547C0.396,-0.565 0.382,-0.58 0.364,-0.59C0.346,-0.599 0.323,-0.604 0.295,-0.604C0.257,-0.604 0.227,-0.597 0.207,-0.581C0.186,-0.565 0.175,-0.543 0.175,-0.516C0.175,-0.496 0.18,-0.48 0.188,-0.468C0.197,-0.455 0.212,-0.444 0.235,-0.435C0.257,-0.425 0.289,-0.415 0.33,-0.404C0.387,-0.389 0.432,-0.372 0.465,-0.353C0.498,-0.333 0.522,-0.31 0.536,-0.284C0.55,-0.257 0.558,-0.225 0.558,-0.187C0.558,-0.146 0.547,-0.111 0.527,-0.081C0.507,-0.051 0.478,-0.028 0.44,-0.012C0.403,0.004 0.359,0.012 0.307,0.012Z" style="fill:rgb(101,101,101);fill-rule:nonzero;"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

+66
View File
@@ -0,0 +1,66 @@
{
"fill" : {
"automatic-gradient" : "gray:0.75000,1.00000",
"orientation" : {
"start" : {
"x" : 0.5,
"y" : 0
},
"stop" : {
"x" : 0.5,
"y" : 0.7
}
}
},
"groups" : [
{
"layers" : [
{
"fill-specializations" : [
{
"value" : {
"solid" : "extended-gray:0.00000,1.00000"
}
},
{
"appearance" : "dark",
"value" : {
"solid" : "extended-gray:1.00000,1.00000"
}
},
{
"appearance" : "tinted",
"value" : {
"solid" : "extended-gray:0.50000,1.00000"
}
}
],
"glass" : true,
"image-name" : "beenvoice.svg",
"name" : "beenvoice",
"position" : {
"scale" : 1.85,
"translation-in-points" : [
0,
0
]
}
}
],
"shadow" : {
"kind" : "neutral",
"opacity" : 0.5
},
"translucency" : {
"enabled" : true,
"value" : 0.5
}
}
],
"supported-platforms" : {
"circles" : [
"watchOS"
],
"squares" : "shared"
}
}