107 lines
4.8 KiB
Markdown
107 lines
4.8 KiB
Markdown
# ten99timecard
|
||
|
||
Income logging, projections, and quarterly estimated tax calculations for US
|
||
1099 contractors. Entirely client-side by default — your data is encrypted with
|
||
your password and stored in a browser cookie. Optionally sync to a self-hosted
|
||
MongoDB backend (still encrypted end-to-end; the server never sees plaintext).
|
||
|
||
**Not tax advice.** Calculations are federal-only approximations. Talk to a CPA.
|
||
|
||
---
|
||
|
||
## What's inside
|
||
|
||
| Page | What it does |
|
||
| --- | --- |
|
||
| **Dashboard** | Configurable stat widgets + charts. YTD income, projected annual tax, next quarterly due date. |
|
||
| **Ledger** | Three tabs: **Work Log** (non-taxable billable record — flat amount *or* hours×rate), **Payments** (taxable 1099 income), **Expenses** (with deductible flag). Each is a year→month→day→item collapsible spreadsheet with global expand-to-level controls. Period summaries show totals, per-child averages, and linear projections. |
|
||
| **Tax** | Full estimated-tax breakdown: SE tax (with SS wage-base cap + additional Medicare), QBI deduction, progressive federal brackets, safe harbor (100%/110% rule), quarterly schedule with past-due flags. Fields you haven't filled in (prior-year AGI, W-2 withholding, etc.) appear as **highlighted prompts** explaining why they'd sharpen the estimate. |
|
||
| **Timer** | Billable-work stopwatch. Start / Pause / Split / Reset. Each split captures the rate *at split time* (changing the rate never rewrites history). Editable split table with two-step confirm. One-click push to Work Log. Logged rows get a badge. **Crash-proof:** state is heartbeated to a cookie every second; if the page reloads while running, a red banner shows exactly when you crashed, when you came back, and the gap — with a button to subtract the gap or keep it (because you were working during the outage). |
|
||
| **Settings** | Theme picker (10 themes × dark/light), default hourly rate, storage mode (cookie / file / cloud), encrypted file import/export. |
|
||
|
||
### Themes
|
||
Standard · Sakura · Pumpkin · Fall · Aqua · Lavender · Comic · Manga · High Contrast · Cyberpunk — each in dark & light.
|
||
|
||
---
|
||
|
||
## Quick start
|
||
|
||
```bash
|
||
npm install
|
||
npm run dev # client on http://localhost:5173
|
||
```
|
||
|
||
The app works out of the box with **cookie storage**. No server, no database,
|
||
no account. Open the page, create a local vault (username + password), start
|
||
logging. Your password derives an AES-256-GCM key via PBKDF2 (100k rounds);
|
||
the cookie holds only ciphertext.
|
||
|
||
### Run the cloud sync server (optional)
|
||
|
||
```bash
|
||
cp server/.env.example server/.env # edit MONGO_URI + JWT_SECRET
|
||
npm run dev:server # Express on http://localhost:4000
|
||
```
|
||
|
||
Then switch storage mode to **Cloud** in Settings. The server stores one opaque
|
||
blob per user. Plaintext never leaves the browser.
|
||
|
||
### Google OAuth (optional)
|
||
|
||
Fill `GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET` in `server/.env`. Without these
|
||
the server still serves email+password auth.
|
||
|
||
---
|
||
|
||
## Tests
|
||
|
||
```bash
|
||
npm test # 155 tests: 135 client (Vitest+RTL) + 20 server (supertest+mongodb-memory-server)
|
||
npm run build # tsc --noEmit && vite build, both workspaces
|
||
```
|
||
|
||
Coverage spans the tax engine, crypto round-trip, cookie chunking, statistics
|
||
rollup/projection, timer crash recovery, store CRUD, and component interaction.
|
||
|
||
---
|
||
|
||
## Stack
|
||
|
||
**Client:** Vite · React 18 · TypeScript 5 · Zustand · React Router · Recharts · date-fns · Web Crypto API
|
||
**Server:** Express · MongoDB · bcrypt · jsonwebtoken · Passport (Google OAuth) · zod
|
||
**Testing:** Vitest · React Testing Library · jsdom · supertest · mongodb-memory-server
|
||
|
||
Everything is MIT/Apache/BSD licensed.
|
||
|
||
---
|
||
|
||
## Tax math (summary)
|
||
|
||
- **SE tax** = 15.3% of `netProfit × 0.9235`
|
||
(12.4% Social Security up to wage base, 2.9% Medicare uncapped, +0.9% additional Medicare above threshold)
|
||
- **Half of SE tax** is an above-the-line deduction
|
||
- **QBI §199A** = 20% of qualified business income, limited to 20% of taxable income, phased out above threshold
|
||
- **Taxable income** = gross − deductible expenses − ½SE − QBI − standard deduction − other adjustments
|
||
- **Safe harbor** = pay the lesser of 90% of current-year tax or 100% (110% if prior AGI > $150k) of prior-year tax, split across four quarters
|
||
- Tax year data shipped for **2024** and **2025**; unknown years fall back to closest
|
||
|
||
State tax is intentionally out of scope.
|
||
|
||
---
|
||
|
||
## Storage model
|
||
|
||
| Mode | Where | Encrypted? |
|
||
| --- | --- | --- |
|
||
| Cookie (default) | `document.cookie`, chunked at ~3.8KB, mirrored to localStorage | ✅ AES-256-GCM |
|
||
| File | Download/upload `.t99` blob | ✅ Same key derivation |
|
||
| Cloud | `PUT /api/data` on your server | ✅ Encrypted *before* upload |
|
||
|
||
Ciphertext format: `base64( salt[16] || iv[12] || aes-gcm-ciphertext )`.
|
||
Lose your password → lose your data. That's the point.
|
||
|
||
---
|
||
|
||
## License
|
||
|
||
MIT
|