$ Invoice3
Invoice Info
Time Tracker
Line Items
Preview
All Invoices
My Info
Help
Invoice Details
Client (Bill To)
Hourly Rate
Work Days on This Invoice
0 days
No work days logged yet.
Use the Time Tracker tab to add days.
Invoice Total
$0.00
Hours: 0.00 Labor: $0.00 Fees: $0.00
Current Time
--:--
Press "Clock In" to start tracking
Today's Session
No segments yet. Clock In to start.
Add Segment Manually
"Less" subtracts break time from hours worked — clock times are not changed.
Add Line Item
Current Line Items
No flat-fee line items yet.
Invoice Totals
Hours
0.00
Labor
$0.00
Grand Total
$0.00
All Invoices
Drag to reorder

Drag any row to reorder. Click an invoice to open it. Delete removes it permanently.

No invoices yet.
Your Information (appears on every invoice)
Invoice Defaults
Saved Clients
No clients saved yet. They are remembered automatically when you save an invoice.
☁ GitHub Sync

Paste your GitHub Personal Access Token (gist scope only) to enable automatic cloud sync. Your data is saved to a private GitHub Gist — invisible to anyone without the token.

On your Mac (first time):
1. Paste your GitHub token above → click 💾 Save Token
2. Click ☁ Sync Now — creates the Gist and fills in the Gist ID
3. Copy the Gist ID (it appears above after sync)

On your iPhone:
1. Paste the same GitHub token → click 💾 Save Token
2. Paste the Gist ID from your Mac
3. Click 💾 Save Token again to save the Gist ID
4. Click ⬇ Pull from Gist — your invoices appear

Every day after that:
Data syncs automatically in the background on every save.
Export / Import Data

Export saves all your invoices, clients, and settings to a file. Import restores from that file on any device. Use AirDrop or iCloud Drive to move the file between devices.

🚀 Getting Started
  1. Go to My Info → fill in your name, business, address, phone, email, and invoice prefix (e.g. INV-). Click Save My Info.
  2. Click + New Invoice in the top bar.
  3. Fill in the client info on the Invoice Info tab.
  4. Go to Time Tracker → clock in and out, then click Add This Day to Invoice.
  5. Go to Preview → review the invoice, then click Print / Save PDF.
🧾 Invoice Info Tab
  • Invoice # — auto-generated from your prefix and counter (set in My Info). Edit freely.
  • Date — invoice date. Defaults to today.
  • Due Date — stored for your records; the invoice prints Terms: Net 30 instead.
  • Terms — editable (Net 30, Due on Receipt, etc.). Appears on the printed invoice.
  • Status badge (top right of the card) — click to cycle: DraftSentPaid. Also clickable in the All Invoices tab.
  • Load a recent client dropdown — pick any saved client to autofill all fields including the hourly rate.
  • Clear button — wipes all client fields blank.
  • Client address is formatted for a standard envelope window when printed.
  • Set your rate here. It autofills when you load a saved client (if a rate was saved with them).
  • Rate is stored per-client in your address book automatically when you save the invoice.
  • Shows all days logged on this invoice. Each entry has Edit and Remove buttons.
  • Edit — loads that day back into the Time Tracker so you can adjust times, break, or description.
⏱ Time Tracker Tab
  • Click Clock In when you start — time rounds to the nearest 5 minutes automatically.
  • Click Clock Out when you stop. Repeat as many times as needed in a day — each pair is one segment.
  • The gap between segments is automatically shown as a break — no math needed.
  • Each green segment row shows start → end and hours worked. Click to remove a segment.
  • Enter Start and End times using the time picker.
  • Less (min) — optional break in minutes. Leave blank for a straight shift. Fills one segment with the break subtracted from total hours. Your real clock times are never changed.
  • Click + Add to add the segment.
  • Fill in the Description of work done, then click Add This Day to Invoice.
  • The date automatically advances by one day so you're ready for the next entry.
  • Repeat across multiple days — all days stack up on the same invoice until the job is done.
💰 Line Items Tab
  • Add flat-fee charges: materials, travel, permits, equipment, anything that isn't hourly.
  • Each item shows as a separate row on the invoice: Description | Amount.
  • The totals bar shows Total Hours, Labor subtotal, and Grand Total.
👁 Preview Tab
  • Shows the formatted invoice exactly as it will print.
  • 🕐 Show Time Detail button — toggles your internal time records (per-day segments, break info). When ON, that detail prints too — good for your own records copy. When OFF, the client sees a clean invoice with just Labor and total amount.
  • Print / Save PDF — opens the browser print dialog. Choose Save as PDF to get a file.
  • Save Invoice — explicitly saves the current invoice and adds the client to your address book.
  • The invoice prints with only two columns: Description | Amount. No rate, no per-day hours — clean and professional.
  • All work descriptions are joined into one paragraph below the Labor line.
🔍 Search Tab
  • Type anything — invoice number, client name, company, or any word from a description.
  • Results show in real time. Click any result to open that invoice.
  • The total shown on each result matches the Preview total exactly.
📋 All Invoices Tab
  • Shows every invoice with its status, total, and action buttons.
  • Status badge — click to cycle Draft → Sent → Paid → Draft without opening the invoice.
  • Outstanding / Paid totals at the top — Outstanding includes all Draft and Sent invoices; Paid shows everything marked Paid.
  • Drag ☰ to reorder invoices. Order saves immediately.
  • Open — loads the invoice into the editor.
  • Delete — permanently removes the invoice after confirmation.
👤 My Info Tab
  • Name, business name, email, phone, and address — appear on every invoice in the header.
  • Prefix — e.g. INV- produces INV-001, INV-002…
  • Next Invoice # — the counter that auto-increments with each new invoice.
  • Clients are saved automatically when you click Save Invoice.
  • + Add Client — manually add a client including their hourly rate.
  • Load — fills the current invoice's client fields and hourly rate.
  • Delete — removes from the address book (doesn't affect invoices).
  • Clear All — wipes the entire client list. Use this if you see duplicate or partial entries from an old bug.
  • ⬇ Export All Data — downloads a invoice-data-YYYY-MM-DD.json file with all invoices, clients, and settings.
  • ⬆ Import Data — picks a previously exported file and merges the data. If duplicates are found, you choose whether to replace with the imported version or keep the existing one.
  • Use AirDrop or iCloud Drive to move the export file between your iPhone and Mac.
  • Automatically syncs your data to a private GitHub Gist so it's always backed up and accessible from any device.
  • Requires a free GitHub account and a Personal Access Token with gist scope.
  • See the GitHub Gist Sync section below for setup instructions.
☁ GitHub Gist Sync — Setup
  • Go to github.com and sign up for a free account if you don't have one.
  1. Sign in to github.com and click your profile photo in the top-right corner.
  2. Click Settings near the bottom of the dropdown menu.
  3. In the left sidebar, scroll all the way to the bottom and click Developer settings.
  4. Click Personal access tokensTokens (classic).
  5. Click Generate new tokenGenerate new token (classic).
  6. Give it a name like Invoice App.
  7. Set Expiration to No expiration so you don't have to repeat this.
  8. Under Select scopes, check gist only — that's the only permission needed.
  9. Scroll down and click Generate token.
  10. Copy the token immediately — GitHub only shows it once. Paste it somewhere safe.
  1. In this app go to My Info → GitHub Gist Sync.
  2. Paste your token into the GitHub Token field and click Save Token.
  3. Click Sync Now — it creates a private Gist and saves the Gist ID automatically.
  4. You'll see a ✓ Last synced message when it works.
  1. Open the app on your other device (Mac, iPad, iPhone).
  2. Go to My Info → GitHub Gist Sync.
  3. Enter the same token and the Gist ID shown on your first device.
  4. Click Pull from Gist — all your invoices and clients will appear.
  • Auto-sync — pushes to the Gist automatically after saving invoices, adding days, deleting, etc.
  • Sync Now — manually pushes everything immediately.
  • Pull from Gist — fetches the latest Gist data and merges it. Safe to run anytime.
  • The Gist is private — only visible to your GitHub account, not the public.
  • If your token expires, generate a new one and update it in My Info.
📱 Using on iPad / iPhone
  • Same WiFi as your Mac — find your Mac's IP in System Settings → WiFi → Details, then open http://192.168.x.x:5500 (or whatever port your server uses) in Safari on the iPad.
  • Add to Home Screen — in Safari, tap Share → Add to Home Screen. Opens fullscreen like a native app.
  • GitHub Gist — the easiest way to keep phone and Mac in sync automatically.
  • Export / Import — export on one device, AirDrop to the other, import. Takes about 10 seconds.
📊 QuickBooks Export
  • Open any invoice and go to the Preview tab.
  • Click the green QB Export button — downloads a .csv file formatted for QuickBooks Online.
  • In QuickBooks Online: Settings ⚙ → Import Data → Invoices → upload the CSV → map columns → Start Import.
  • Each work day and flat-fee line item becomes its own row in the CSV, grouped under the same invoice number.
  • Customer name in the CSV must match your QuickBooks customer name exactly — QBO will create the customer automatically if it doesn't exist yet.
  • Item (Product/Service) is set to Services — a safe generic name. Rename it in QBO after import if you use a different service item name.
  • Labor rows export as: Quantity = hours worked, Rate = your hourly rate, Amount = total.
  • Flat-fee items export as: Quantity = 1, Rate = full amount.
💾 Your Data
  • All data is stored in your browser's localStorage — nothing is sent anywhere unless you use Gist Sync.
  • localStorage is per-browser per-device. Chrome and Safari on the same Mac store data separately.
  • Clearing browser data or site data will erase everything — always keep an export backup.
  • You can store roughly 1,000–2,000 invoices before approaching the 5MB localStorage limit.

Each snippet below is self-contained — copy the whole block, paste into the console, hit Enter.

Check how much storage you're using:

let t=0; for(let k in localStorage){ if(localStorage.hasOwnProperty(k)) t+=(localStorage[k].length*2); } console.log((t/1024).toFixed(2)+' KB used');

List all invoices with their totals:

JSON.parse(localStorage.getItem('inv_list')||'[]').forEach(id=>{const i=JSON.parse(localStorage.getItem('inv_data_'+id)||'{}');const r=parseFloat(i.hourlyRate)||0;const t=(i.days||[]).reduce((s,d)=>s+(parseFloat(d.hours)||0)*r,0)+(i.lineItems||[]).reduce((s,l)=>s+parseFloat(l.amount||0),0);console.log(i.number,'-',(i.client&&i.client.name)||'(no client)','-','$'+t.toFixed(2));});

Back up everything to a downloadable JSON file:

const b={};for(let k in localStorage){if(localStorage.hasOwnProperty(k))b[k]=localStorage.getItem(k);}const a=document.createElement('a');a.href=URL.createObjectURL(new Blob([JSON.stringify(b,null,2)],{type:'application/json'}));a.download='invoice3-backup-'+new Date().toISOString().split('T')[0]+'.json';a.click();

Restore from a backup file:

const inp=document.createElement('input');inp.type='file';inp.accept='.json';inp.onchange=e=>{const r=new FileReader();r.onload=x=>{const d=JSON.parse(x.target.result);Object.keys(d).forEach(k=>localStorage.setItem(k,d[k]));console.log('Restored '+Object.keys(d).length+' keys — reload the page.');};r.readAsText(e.target.files[0]);};inp.click();

Delete one invoice by number (e.g. INV-003):

const num='INV-003'; const list=JSON.parse(localStorage.getItem('inv_list')||'[]'); const id=list.find(i=>{try{return JSON.parse(localStorage.getItem('inv_data_'+i)||'{}').number===num;}catch(e){return false;}}); if(id){localStorage.removeItem('inv_data_'+id);localStorage.setItem('inv_list',JSON.stringify(list.filter(i=>i!==id)));console.log('Deleted '+num+' — reload the page.');}else{console.log('Invoice not found: '+num);}