Add 'apps/mobile/' from commit '5fa30f365f21531094cd4d2045042bb4f1370ac3'

git-subtree-dir: apps/mobile
git-subtree-mainline: 86f8987dff
git-subtree-split: 5fa30f365f
This commit is contained in:
2026-08-16 21:42:59 -04:00
222 changed files with 23436 additions and 0 deletions
+290
View File
@@ -0,0 +1,290 @@
# App Store Connect — beenvoice iOS
Copy-paste reference for submitting **beenvoice** (`com.beenvoice.app`, v1.0.0). Production web host: `beenvoice.app`.
---
## App Information
| Field | Value |
|-------|--------|
| **Name** | beenvoice |
| **Subtitle** (30 chars max) | Invoices & time tracking |
| **Bundle ID** | `com.beenvoice.app` |
| **SKU** | `beenvoice-ios` (your choice; immutable) |
| **Primary language** | English (U.S.) |
| **Primary category** | Business |
| **Secondary category** | Productivity |
| **Content rights** | Does not contain third-party content |
| **Age rating** | 4+ (no restricted content; business/finance utility) |
### Copyright
```
© 2026 beenvoice
```
---
## URLs
Deploy the Next.js legal pages before submission. Privacy Policy and Terms must load without login.
| Field | URL |
|-------|-----|
| **Privacy Policy URL** | `https://beenvoice.app/privacy` |
| **Terms of Use (EULA)** | Use Apple Standard EULA *or* link `https://beenvoice.app/terms` |
| **Support URL** | `https://beenvoice.app` (or a dedicated `/support` page when available) |
| **Marketing URL** (optional) | `https://beenvoice.app` |
---
## Promotional Text (170 chars max)
Optional; can be changed without a new build.
```
Track billable hours, manage clients, and send invoices from your phone. Syncs with your beenvoice account. Lock the app with Face ID.
```
---
## Description (4000 chars max)
```
beenvoice is the mobile companion for freelancers and small teams who invoice clients and track billable time.
DASHBOARD AT A GLANCE
See revenue, pending and overdue invoices, and your running timer without opening multiple tools.
TIME CLOCK
Clock in and out with an optional description, client, invoice, and hourly rate. On iPhone, a Live Activity on the Lock Screen and Dynamic Island shows elapsed time while you work.
INVOICES
Browse, filter, create, and edit invoices. Update status and keep billing moving from anywhere.
CLIENTS & BUSINESSES
Maintain client records and business profiles so invoices stay consistent across web and mobile.
MULTI-ACCOUNT
Switch between beenvoice accounts (e.g. work and personal) with separate sessions, similar to a password manager.
SECURITY
Optional per-account app lock with PIN and Face ID / Touch ID when returning to the app.
OFFICIAL OR SELF-HOSTED
Sign in to the official beenvoice cloud or point the app at your own beenvoice server URL.
REQUIREMENTS
A beenvoice account and network access to your beenvoice server. The mobile app is not a standalone product—it connects to the same API as the beenvoice web app.
Questions or feedback: support via your beenvoice administrator or the contact on beenvoice.app.
```
---
## Keywords (100 chars max, comma-separated, no spaces after commas)
```
invoice,time tracking,freelance,billing,clients,timer,accounting,small business,hours,beenvoice
```
---
## Whats New (Version 1.0.0)
```
Initial App Store release.
• Dashboard with revenue and invoice summaries
• Time clock with optional client, invoice, and rate
• iOS Live Activity for running timers
• Invoice list, create, and edit
• Clients and businesses management
• Multi-account support with secure sign-in
• Per-account app lock (PIN and Face ID)
• Light and dark appearance
```
---
## App Review Information
### Sign-in required
**Yes** — the app requires a beenvoice account.
### Demo account (production server)
Ensure migrations through `0028_enable_public_demo_password` have run on the server reviewers will hit.
| Field | Value |
|-------|--------|
| **Username** | `demo@example.com` |
| **Password** | `demo123` |
### Notes for Review
```
beenvoice is a client for the beenvoice invoicing and time-tracking platform (web + API).
SIGN IN
1. Open the app.
2. Leave "Official" server selected (https://beenvoice.app) unless we specify otherwise in this note.
3. Sign in with the demo account above.
WHAT TO TEST
• Dashboard — sample invoices and stats are pre-seeded.
• Timer tab — clock in, optionally pick client/description; on a physical device, Lock Screen Live Activity appears while a timer runs.
• Invoices — list includes draft, sent, and paid examples.
• Settings — profile, theme, optional app lock (PIN / Face ID).
ACCOUNT DELETION
Settings → Delete account offers permanent in-app account deletion without contacting support.
After a destructive confirmation, the server removes the account record, sessions, access keys,
invoices, clients, businesses, recurring invoices, expenses and receipts, time entries, templates,
audit records, and uploaded files. The app then removes the local account and returns to sign-in.
The supplied demo account is shared, so please test deletion last if deletion verification is required.
APP LOCK
Optional. Enable in Settings → App Lock. Face ID uses on-device biometrics only; no biometric data is sent to our servers.
LIVE ACTIVITY
Requires a physical iPhone (not available in Simulator). Start a timer, lock the device, and check the Lock Screen / Dynamic Island.
SELF-HOSTED SERVERS
Users may enter a custom server URL on sign-in. Review uses the official server only.
No in-app purchases. No ads.
```
Update the official server URL in the note if you change `DEFAULT_API_URL` in `lib/config.ts`.
---
## App Privacy (Privacy Nutrition Labels)
Answer in App Store Connect → App Privacy. Adjust if you add analytics later.
### Data linked to the user
| Data type | Purpose | Collected | Linked | Tracking |
|-----------|---------|-----------|--------|----------|
| **Email address** | App functionality, account | Yes | Yes | No |
| **Name** | App functionality, account | Yes | Yes | No |
| **Other user content** (clients, invoices, time entries, business details) | App functionality | Yes | Yes | No |
| **User ID** | App functionality | Yes | Yes | No |
### Data not collected for tracking
The app does **not** use data for tracking across apps/websites. No third-party analytics SDKs in the current build.
### Data collected but not linked (typically none)
If you only use on-device Face ID via `expo-local-authentication`, Apple treats biometrics as **not** collected by the developer—do **not** declare Face ID templates as collected data.
### Practice to select
- **Data Used to Track You:** None
- **Data Linked to You:** Contact info, identifiers, user content (as above)
- **Data Not Linked to You:** None (unless you add crash logs without account linkage)
---
## Age Rating Questionnaire (typical answers)
| Topic | Answer |
|-------|--------|
| Cartoon / fantasy violence | None |
| Realistic violence | None |
| Sexual content | None |
| Profanity | None |
| Drugs, alcohol, tobacco | None |
| Gambling | None |
| Horror | None |
| Mature / suggestive themes | None |
| Unrestricted web access | No (in-app browser not used for open web) |
| User-generated content broadly distributed | No (invoice data is private to the account) |
Expected result: **4+**.
---
## Export Compliance
In App Store Connect encryption questions:
- **Uses encryption:** Yes (HTTPS/TLS for API)
- **Exempt:** Yes — standard HTTPS only, qualify for exemption under mass-market encryption rules (same as most apps using TLS)
Confirm annually in Connect; no separate ERN needed for standard TLS-only apps in most cases.
---
## Screenshots (required sizes)
Capture from **iPhone 6.7"** (e.g. iPhone 15 Pro Max) and **6.5"** if you support older requirements. Xcode Simulator → Save Screenshot, or physical device.
Suggested screens (portrait):
1. **Sign-in** — brand, clean auth (optional; some teams skip)
2. **Dashboard** — stats + recent invoices (demo account)
3. **Timer** — running or ready to clock in
4. **Invoices** — list with statuses
5. **Invoice detail / edit** — line items
6. **Settings** — theme + app lock (shows polish)
Minimum: **3 screenshots** per required device size.
Optional: iPad 12.9" if `supportsTablet: true` — use iPad simulator or “Run on iPad” with scaled iPhone UI.
---
## Build & submit
### Option A — Local Xcode (no EAS)
See **[IOS_LOCAL_RELEASE.md](./IOS_LOCAL_RELEASE.md)** for the full guide.
```bash
cd beenvoice-app
cp .ios-release.env.example .ios-release.env # once — add Team ID + API key
bun run ios:release:upload # archive + upload to TestFlight
```
Requires Xcode on macOS, Apple Developer membership, and an App Store Connect API key.
### Option B — EAS (Expo cloud build)
```bash
cd beenvoice-app
# Production iOS build (auto-increments build number)
eas build --platform ios --profile production
# Submit latest build to App Store Connect
eas submit --platform ios --profile production
```
Prerequisites:
- Apple Developer Program membership
- App record created in App Store Connect with bundle ID `com.beenvoice.app`
- EAS credentials configured (`eas credentials`) — Option B only
- Privacy Policy URL live and reachable
---
## Pre-submission checklist
- [ ] Legal pages live at Privacy Policy URL (HTTP 200, no auth wall)
- [ ] Demo account works on production API (`demo@example.com` / `demo123`)
- [ ] `eas build --profile production` succeeds
- [ ] TestFlight smoke test on device (login, timer, invoices, app lock)
- [ ] Live Activity tested on physical iPhone
- [ ] App Privacy answers match actual data flows
- [ ] In-app account deletion succeeds from Settings and returns to sign-in
- [ ] Screenshots uploaded for required device sizes
- [ ] Review notes include demo credentials and server URL
- [ ] Export compliance answered
- [ ] Version `1.0.0` matches `app.json` / Connect version field
+276
View File
@@ -0,0 +1,276 @@
# beenvoice-app architecture
Dense reference for the Expo 57 mobile companion. Talks to **beenvoice** over tRPC + better-auth. Requires a **development build** (not Expo Go) for widgets, SecureStore auth, and biometrics.
## Stack
| Layer | Technology |
|-------|------------|
| Framework | Expo 57, expo-router 57 (file-based routes) |
| UI | React Native 0.85, `@expo/ui` (SwiftUI widgets) |
| API | tRPC 11 + TanStack Query, SuperJSON |
| Auth | better-auth + `@better-auth/expo``expo-secure-store` |
| Types | `AppRouter` imported from `../beenvoice-web/src/server/api/root` |
## Boot sequence
```
app/_layout.tsx
SafeAreaProvider
ThemeProvider ← AsyncStorage color mode
BrandBackground + StatusBar
AccountsProvider ← load accounts, active id, apiUrl
AppServices ← key={activeAccountId:apiUrl}
AuthProvider ← better-auth client (storagePrefix)
TRPCProvider ← cookie header on /api/trpc
RootNavigator
session? → (app) | (auth)
```
`AccountsProvider` blocks on `LoadingScreen` until AsyncStorage hydrates. `AuthProvider` + `TRPCProvider` **remount** when `activeAccountId` or `apiUrl` changes so each account uses isolated SecureStore session keys.
## Routing
### Auth group `app/(auth)/`
| Screen | File | Notes |
|--------|------|-------|
| redirect | `index.tsx` | → sign-in |
| Sign in | `sign-in.tsx` | Server picker, deferred validation |
| Register | `register.tsx` | REST register + sign-in + finalize account |
| Forgot password | `forgot-password.tsx` | REST |
| Reset password | `reset-password.tsx` | Deep link `beenvoice://reset-password?token=` |
### App group `app/(app)/`
`NativeTabs` (5 tabs) in `_layout.tsx`. Wrapped in `AppLockProvider` + `AppLockOverlay`.
| Tab | Screen | Features |
|-----|--------|----------|
| Dashboard | `index.tsx` | Stats, running timer chip, recent invoices |
| Timer | `timer.tsx` | `TimeClockPanel` |
| Entities | `entities/*` | Clients + businesses CRUD stacks |
| Invoices | `invoices/*` | List, create, edit, status |
| More | `more/*` | Expenses, reports, recurring invoices, time entries, settings |
Nested stacks: `entities/_layout.tsx`, `invoices/_layout.tsx`, `more/_layout.tsx`.
## Multi-account model
**Storage**`lib/accounts.ts` (AsyncStorage):
| Key | Content |
|-----|---------|
| `beenvoice:accounts` | `SavedAccount[]` |
| `beenvoice:active-account-id` | current account id or absent |
| `beenvoice:draft-instance-url` | server URL before first login |
**Account id**: `{hostname}::{userId}` — host from instance URL without protocol/trailing slash.
**Auth storage prefix** (SecureStore, better-auth):
| Mode | Prefix |
|------|--------|
| Guest (signed out / adding account) | `beenvoice:guest` |
| Per account | `beenvoice:auth:{accountId}` |
Keys written by `@better-auth/expo`: `{prefix}_cookie`, `{prefix}_session_data`, `{prefix}_last_login_method` (colons normalized to `_` in SecureStore). Large cookies are chunked (`\x01ba-chunks:N` + `.{i}` keys).
### Sign-in flow (critical)
1. User signs in while `AuthProvider` uses **guest** (or current) prefix.
2. Session lands in that prefix's SecureStore.
3. `finalizeAuthenticatedAccount()` (`lib/auth-storage.ts`):
- `migrateAuthStorage(sourcePrefix → targetPrefix)` — copy session keys
- `registerAccount()` — set active account, persist metadata
4. `AuthProvider` remounts with account prefix; session already migrated → `RootNavigator` shows `(app)`.
Without migration, remounting loses the session and forces a second login.
### Account switcher
`components/AccountSwitcher.tsx` (header):
- **Switch**: `switchAccount(id)` → remount auth/tRPC with that account's URL + prefix (session must already exist in target prefix).
- **Add account**: `signOut()` + `clearActiveAccount()` → auth stack on guest prefix.
## Server picker
`components/AuthServerPicker.tsx` + `lib/server-mode.ts`:
- **Official** — `DEFAULT_API_URL` (`https://beenvoice.app` in `lib/config.ts`)
- **Self-hosted** — user URL, normalized via `lib/instance-url.ts` (adds `http://` for localhost/LAN)
`setInstanceUrl()` updates runtime API (`lib/config.ts` `setRuntimeApiUrl`) and draft or active account URL.
## API URL resolution
`lib/config.ts` priority:
1. Runtime override (`setRuntimeApiUrl` from AccountsContext)
2. `EXPO_PUBLIC_API_URL` from `.env`
3. Dev: Metro host IP + `:3000`
4. `DEFAULT_API_URL`
## tRPC client
`lib/trpc.tsx`:
```ts
httpBatchLink({
url: `${apiUrl}/api/trpc`,
transformer: SuperJSON,
headers: () => ({ cookie: authClient.getCookie() }),
})
```
Query defaults: `staleTime: 30_000`, `retry: 1`. Usage: `import { api } from "@/lib/trpc"`.
## Offline mode
Read queries are cached per account + API URL in AsyncStorage (`lib/offline-cache.ts`). `TRPCProvider` restores that cache before mounting app screens, then persists successful query data for up to 7 days. SuperJSON is used for persistence so API payloads keep Date values and other transformed types.
`lib/network-status.ts` connects `expo-network` to TanStack Query's `onlineManager`. When the device is offline, queries pause instead of repeatedly failing; when connectivity returns, React Query reconnect behavior refreshes stale data. The better-auth Expo client also keeps its SecureStore session cache enabled so a previously signed-in account can get through app boot while offline, then reconcile with the server when connectivity returns.
Account removal clears that account's persisted query cache alongside SecureStore auth and time-clock preferences.
Current offline scope: previously loaded dashboard, invoices, clients, businesses, expenses, reports, recurring invoices, and time entries can be viewed offline from cache. Mutations still require the API; timer actions, invoice edits/status changes, receipt uploads, reminders, emails, and onboarding writes are not persisted as an offline queue yet because they need conflict and side-effect rules.
## App lock (per account)
`lib/app-lock.ts` — SecureStore keys scoped by `activeAccountId`:
- `beenvoice:app-lock:{id}:enabled|pin|biometric`
- One-time migration from legacy global keys `beenvoice_app_lock_*`
`contexts/AppLockContext.tsx`:
- Hydrates on account change
- Locks when returning from background (if enabled)
- PIN 46 digits; Face ID / Touch ID via `expo-local-authentication`
- Only active inside `(app)/_layout.tsx` — auth screens never locked
UI: `AppLockOverlay.tsx`, `PinPrompt.tsx`, settings toggles.
## Time clock
`components/time-clock/TimeClockPanel.tsx`:
- Client required; description optional (defaults to **"Clock In"** via `lib/time-clock.ts`)
- Optional invoice, hourly rate, backdated start
- `clockOut` sends optional description update
- Syncs iOS Live Activity when timer metadata changes (client, invoice, description); timer uses native `timerInterval` on the lock screen
### Live Activity
| File | Role |
|------|------|
| `widgets/TimeClockActivity.tsx` | SwiftUI widget (`expo-widgets`); must keep all UI **inside** the `"widget"` function (babel preset serializes only that) |
| `lib/time-clock-live-activity.ts` | `syncTimeClockLiveActivity`, `endTimeClockLiveActivity` |
| `app.json` | Plugin `expo-widgets`, app group `group.com.beenvoice.app` |
Does not work in Expo Go — use `bun run ios` dev build.
## Theming
| File | Role |
|------|------|
| `lib/beenvoice-theme.ts` | Light tokens mirrored from web `globals.css` |
| `lib/theme-palette.ts` | Light + dark `ThemeColors` |
| `constants/theme.ts` | spacing, radii, fonts (Playfair / Inter / SpaceMono) |
| `contexts/ThemeContext.tsx` | system / light / dark → AsyncStorage `beenvoice:color-mode` |
| `components/BrandBackground.tsx` | Grid + animated blob |
| `lib/use-themed-styles.ts` | Memoized StyleSheet factory |
## Contexts summary
| Context | File |
|---------|------|
| Auth | `contexts/AuthContext.tsx` |
| Accounts | `contexts/AccountsContext.tsx` |
| App lock | `contexts/AppLockContext.tsx` |
| Theme | `contexts/ThemeContext.tsx` |
## `lib/` module index
| Module | Purpose |
|--------|---------|
| `accounts.ts` | Account registry types + AsyncStorage |
| `auth-storage.ts` | Session migration, `finalizeAuthenticatedAccount` |
| `auth-api.ts` | REST register / forgot / reset |
| `config.ts` | API URL |
| `instance-url.ts` | URL normalization + persistence |
| `server-mode.ts` | Official vs self-hosted |
| `trpc.tsx` | tRPC provider |
| `app-lock.ts` | Per-account PIN storage |
| `form-validation.ts` | Validators + `useFieldVisibility` |
| `format.ts` | Currency, dates |
| `invoice-status.ts` | Status colors |
| `invoice-number.ts` | Number generation |
| `time-clock.ts` | Timer formatting, clock-out copy |
| `time-clock-live-activity*.ts` | Live Activity bridge |
| `tab-layout.ts`, `tab-bar-insets.ts`, `top-chrome-insets.ts` | Layout metrics |
## Components layout
```
components/
├── ui/ # Button, Card, Input, SelectField, DateTimeField
├── time-clock/ # TimeClockPanel
├── clients/ # ClientForm
├── businesses/ # BusinessForm
├── invoices/ # LineItemEditor
├── AuthServerPicker, AccountSwitcher, TopChrome, AppLockOverlay, Logo, …
```
## Native config
**`app.json`**
- `scheme`: `beenvoice`
- `bundleIdentifier`: `com.beenvoice.app`
- Plugins: dev-client, router, secure-store, widgets, local-authentication
- iOS Face ID usage strings; custom `beenvoice.icon`
**`eas.json`**
- Profiles: development, preview, production
- `cli.appVersionSource`: remote
- User does not require EAS for local `expo run:ios`
**Metro**: port **8082** (`package.json` scripts) to avoid collisions.
## Deep links
| URL | Handler |
|-----|---------|
| `beenvoice://reset-password?token=…` | `reset-password.tsx` |
| `beenvoice://timer` | Live Activity tap → timer tab |
## Development commands
```bash
bun run ios # build + run simulator, Metro :8082
bun run start # Metro only (--dev-client --port 8082)
bunx expo prebuild --platform ios --clean # after native dep / icon changes
```
## Troubleshooting
| Symptom | Likely cause |
|---------|----------------|
| `PlatformConstants` / runtime not ready | Wrong Metro port, stale native build, or Expo Go instead of dev client |
| Login twice | Session not migrated — see `finalizeAuthenticatedAccount` |
| Live Activity blank | Widget UI outside `"widget"` function; rebuild native |
| API unreachable on device | Use LAN IP in `EXPO_PUBLIC_API_URL`, not `localhost` |
| Auth fails on device | `BETTER_AUTH_URL` on server must match reachable host |
## Server dependency
Requires beenvoice with:
- `@better-auth/expo` in `src/lib/auth.ts`
- `trustedOrigins` including `beenvoice://` and `exp://`
- Postgres running (`docker compose -f docker-compose.dev.yml up -d db`)
See [beenvoice-web/docs/ARCHITECTURE.md](../../beenvoice-web/docs/ARCHITECTURE.md).
+88
View File
@@ -0,0 +1,88 @@
# Local iOS release (no EAS)
Archive and upload **beenvoice** to App Store Connect using Xcode on your Mac — no Expo Application Services (EAS) subscription required.
## Prerequisites
- macOS with **Xcode** (same major version you use for development)
- **Apple Developer Program** membership
- App record in [App Store Connect](https://appstoreconnect.apple.com) with bundle ID `com.beenvoice.app`
- **Distribution** signing set up in Xcode (automatic signing + team is enough for most cases)
- [App Store Connect API key](https://appstoreconnect.apple.com/access/integrations/api) (for upload only)
## One-time setup
```bash
cd beenvoice-app
cp .ios-release.env.example .ios-release.env
```
Edit `.ios-release.env`:
| Variable | Where to find it |
|----------|------------------|
| `APPLE_TEAM_ID` | [developer.apple.com/account](https://developer.apple.com/account) → Membership → Team ID |
| `APP_STORE_CONNECT_API_KEY_ID` | App Store Connect → Users and Access → Integrations → Keys |
| `APP_STORE_CONNECT_API_ISSUER_ID` | Same page (Issuer ID at top) |
| `APP_STORE_CONNECT_API_KEY_PATH` | Path to downloaded `AuthKey_XXXXXX.p8` |
| `EXPO_PUBLIC_API_URL` | Production API URL baked into the release bundle |
Optional: store the `.p8` in `~/.appstoreconnect/private_keys/` (never commit it).
Open the iOS project once in Xcode and confirm **Signing & Capabilities** succeeds for targets **beenvoice** and **ExpoWidgetsTarget**.
## Commands
```bash
# Archive + export signed IPA to dist/ios-release/export/
bun run ios:release
# Archive + export + upload to App Store Connect (TestFlight)
bun run ios:release:upload
```
### Flags (pass through to the script)
```bash
bash scripts/ios-release.sh --archive-only # .xcarchive only
bash scripts/ios-release.sh --export-only --upload # re-upload existing archive
bash scripts/ios-release.sh --no-prebuild # skip expo prebuild
bash scripts/ios-release.sh --no-bump # don't increment build number
```
With `IOS_BUMP_BUILD=1` in `.ios-release.env`, each run bumps `CFBundleVersion` via `agvtool` (recommended for repeated TestFlight uploads).
## What the script does
1. `expo prebuild --platform ios` (unless `--no-prebuild`)
2. `pod install`
3. Optional build-number bump (`agvtool`)
4. `xcodebuild archive` (Release, generic iOS device)
5. `xcodebuild -exportArchive` → App Store IPA
6. `xcrun altool --upload-app` (with `--upload` only)
Artifacts land in `dist/ios-release/` (gitignored).
## After upload
1. App Store Connect → **TestFlight** — wait for “Processing” to finish
2. Smoke-test on device
3. Submit for App Store review when ready
See also [APP_STORE_CONNECT.md](./APP_STORE_CONNECT.md) for metadata, screenshots, and review notes.
## Notes
- **Dev client:** `expo-dev-client` is in the native project today. Store builds still work, but the binary includes the dev client shell. For a slimmer production binary, remove that plugin and re-run prebuild before release (or maintain a separate `app.config` variant).
- **Manual upload:** After `bun run ios:release`, drag the IPA into Apples [Transporter](https://apps.apple.com/app/transporter/id1450874784) app instead of using `--upload`.
- **CI:** Run the same script on a Mac runner (GitHub `macos-latest`, etc.) with secrets injected as env vars instead of `.ios-release.env`.
## Troubleshooting
| Issue | Fix |
|-------|-----|
| No signing certificate | Xcode → Settings → Accounts → Download Manual Profiles; or open project and enable automatic signing |
| `pod install` fails | `cd ios && pod repo update && pod install` |
| Upload auth error | Verify API key has **Developer** access; check Key ID, Issuer ID, and `.p8` path |
| Duplicate build number | Enable `IOS_BUMP_BUILD=1` or bump `CURRENT_PROJECT_VERSION` in Xcode |
| Widget extension signing | Both **beenvoice** and **ExpoWidgetsTarget** need the same team |
+12
View File
@@ -0,0 +1,12 @@
# beenvoice-app documentation
| Document | Description |
|----------|-------------|
| [ARCHITECTURE.md](./ARCHITECTURE.md) | Routing, contexts, auth/accounts, tRPC, app lock, Live Activity, theming |
| [../README.md](../README.md) | Setup, run, troubleshooting |
| [../AGENTS.md](../AGENTS.md) | Agent conventions |
## Related
- [beenvoice-web docs](../../beenvoice-web/docs/README.md) — server API and web app
- [Workspace README](../../README.md) — full-stack layout