const { useState, useEffect, useCallback, useRef } = React; /* ── Supabase storage ── */ const _db = supabase.createClient("https://befxwolodujpjvehvvdf.supabase.co", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImJlZnh3b2xvZHVqcGp2ZWh2dmRmIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzYxMzM4NDQsImV4cCI6MjA5MTcwOTg0NH0.AP46knVwqLbJlLURS7wX6IPT3Wh6kV03LSb_t82B5o0"); const dbGet = async (key, fallback) => { try { const { data } = await _db .from('app_storage') .select('value') .eq('key', key) .maybeSingle(); if (!data || data.value == null) return fallback; return JSON.parse(data.value); } catch(e) { return fallback; } }; const dbSet = async (key, value) => { try { await _db.from('app_storage').upsert( { key: key, value: JSON.stringify(value) }, { onConflict: 'key' } ); } catch(e) {} }; /* aliases so existing code works unchanged */ const _ss = dbSet, _sl = dbGet, _s = dbSet, _l = dbGet; const N="#2c3a43", AC="#D4693A", CR="#f4f1ec", W="#fff", BD="#e5dfd5", MU="#9a9189", RE="#c0392b", GR="#1e7e5e", BL="#4E7FA6"; const WA_CITIES=[ {city:"Vancouver", rate:8.9}, {city:"Battle Ground",rate:8.9}, {city:"Camas", rate:8.8}, {city:"La Center", rate:8.8}, {city:"Ridgefield", rate:9.0}, {city:"Washougal", rate:8.8}, {city:"Yacolt", rate:8.7}, {city:"Woodland", rate:7.9}, ]; const cityOpts=WA_CITIES.map(c=>({v:c.city,l:`${c.city} — ${c.rate}%`})); const rateForCity=city=>(WA_CITIES.find(c=>c.city===city)?.rate)||8.9; const DEF_SPLITS={marketing:10,overhead:5,pm_ivan:25,pm_cory:20,pm_cory_bonus:5,pm_yuriy:25,pm_mark:25}; const USERS=[ {id:"u1",username:"admin", name:"Becky", role:"admin"}, {id:"u2",username:"nazar", name:"Nazar", role:"pm"}, {id:"u3",username:"ivan", name:"Ivan", role:"pm"}, {id:"u4",username:"cory", name:"Cory", role:"pm"}, {id:"u5",username:"yuriy", name:"Yuriy", role:"pm"}, {id:"u6",username:"mark", name:"Mark", role:"pm"}, ]; const LOGO="data:image/png;base64,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"; const fmt = n => new Intl.NumberFormat("en-US",{style:"currency",currency:"USD"}).format(n||0); const pctf = n => `${(+n||0).toFixed(1)}%`; const uid = () => Math.random().toString(36).slice(2,10); const today= () => new Date().toISOString().split("T")[0]; const subTot = s => (s.cost||0)+(s.inclTax?((s.cost||0)*(s.taxRate||0))/100:0); const projCost= p => (p.subs||[]).reduce((t,s)=>t+(s.cost||0),0); /* pre-tax sub costs only */ const projBase= p => p.subTotal||p.value||0; /* pre-tax revenue */ const isDebit= s => (s.company||"").trim().toLowerCase()==="debit card transaction"; const getPmPct= (uname,est,sp) => { if(uname==="cory") return (sp.pm_cory||20)+(est?(sp.pm_cory_bonus||5):0); if(uname==="ivan") return sp.pm_ivan||25; if(uname==="yuriy") return sp.pm_yuriy||25; if(uname==="mark") return sp.pm_mark||25; return 0; }; async function seedOnce(){ const done=await _l("pr_seeded9",false); if(done)return; await _s("pr_users9",USERS.map(u=>({...u,pass:"Paliy"}))); await _s("pr_seeded9",true); } /* ── Primitives ── */ function Lbl({t}){ return t?
{t}
:null; } function Inp({label,value,onChange,type="text",placeholder="",mb=14}){ const[f,sf]=useState(false); return
onChange(e.target.value)} placeholder={placeholder} onWheel={e=>e.target.blur()} onFocus={()=>sf(true)} onBlur={()=>sf(false)} style={{width:"100%",padding:"12px 14px",background:CR,border:`1.5px solid ${f?AC:BD}`,borderRadius:10,fontFamily:"inherit",fontSize:15,color:N,outline:"none",boxSizing:"border-box",transition:"border-color .15s"}}/>
; } function Sel({label,value,onChange,options,mb=14}){ return
; } function Btn({children,onClick,full,outline,color=N,disabled,sm}){ return ; } function Tog({on,onChange,label,color=AC}){ return
onChange(!on)}>
{label&&{label}}
; } function StateTog({value,onChange}){ return
{[["WA","Washington"],["OR","Oregon"]].map(([v,l])=>( ))}
; } function Sheet({title,onClose,children}){ return
e.target===e.currentTarget&&onClose()}>
{title}
{children}
; } function Badge({label,color,bg}){ return {label}; } function Card({children,mb=0}){ return
{children}
; } function Divider(){ return
; } function SplitRow({l,v,c=MU}){ return
{l} {v}
; } function Empty({text}){ return
{text}
; } /* ── Completion Slider ── */ function CompletionSlider({value=0,onChange,maxValue=100}){ const[local,setLocal]=useState(value||0); const dragging=useRef(false); if(!dragging.current && local!==(value||0)) setLocal(value||0); const col=local===100?GR:local>=50?AC:BL; return
e.stopPropagation()}>
Completion {local}%
{ dragging.current=true; setLocal(Math.min(+e.target.value,maxValue)); }} onPointerUp={e=>{ dragging.current=false; onChange(Math.min(+e.target.value,maxValue)); }} onMouseUp={e=>{ dragging.current=false; onChange(Math.min(+e.target.value,maxValue)); }} style={{position:"absolute",top:-8,left:0,width:"100%",height:24,opacity:0,cursor:"pointer",margin:0,padding:0}}/>
{[0,25,50,75,100].map(v=>maxValue?0.3:1}}>{v}%)}
; } /* ── Auth ── */ function Auth({onLogin}){ const[uname,setUname]=useState(""), [pass,setPass]=useState(""), [err,setErr]=useState(""); const submit=async()=>{ setErr(""); const users=await _l("pr_users9",[]); const u=users.find(x=>x.username.toLowerCase()===uname.toLowerCase().trim()&&x.pass===pass); if(!u){setErr("Incorrect username or password.");return;} const prof=USERS.find(x=>x.id===u.id); onLogin({...u,role:prof?.role||u.role}); }; return
Paliy
Team Sign In
{err&&
{err}
} Sign In
; } /* ── App ── */ function App(){ const[user,setUser]=useState(null); const[tab,setTab]=useState("projects"); const[projs,setProjs]=useState([]); const[subCos,setSubCos]=useState([]); const[splits,setSplits]=useState(DEF_SPLITS); const[selId,setSelId]=useState(null); const[ready,setReady]=useState(false); const lastSave=useRef(0); const scrollRef=useRef(null); const refresh=useCallback(async(force=false)=>{ if(!force&&Date.now()-lastSave.current<30000)return; const[p,c,s]=await Promise.all([_sl("pr_p9",[]),_sl("pr_sc9",[]),_sl("pr_sp9",DEF_SPLITS)]); setProjs(p);setSubCos(c);setSplits(s); },[]); useEffect(()=>{(async()=>{ await seedOnce(); await refresh(true); setReady(true); })();},[]); useEffect(()=>{ if(!ready)return; const id=setInterval(()=>refresh(),30000); return()=>clearInterval(id); },[ready,refresh]); const sp=async v=>{lastSave.current=Date.now();setProjs(v); await _ss("pr_p9",v); lastSave.current=Date.now();}; const sc=async v=>{lastSave.current=Date.now();setSubCos(v); await _ss("pr_sc9",v); lastSave.current=Date.now();}; const ss=async v=>{lastSave.current=Date.now();setSplits(v); await _ss("pr_sp9",v); lastSave.current=Date.now();}; if(!ready)return
; if(!user) return ; const isAdmin=user.role==="admin"; const visible=isAdmin?projs:projs.filter(p=>p.pmId===user.id); const sel=visible.find(p=>p.id===selId); const navTabs=[ {id:"projects", label:"Projects"}, {id:"reports", label:"Reports"}, {id:"settings", label:"Settings"}, ]; const goTab = id => { setTab(id); setSelId(null); if (scrollRef.current) scrollRef.current.scrollTop = 0; }; return
{/* Top bar */}
Paliy
{user.name}
{isAdmin?"Admin":"Project Manager"}
{selId&&sel ?{ setSelId(null); if(scrollRef.current) scrollRef.current.scrollTop=0; }}/> :tab==="projects"?{ setSelId(id); if(scrollRef.current) scrollRef.current.scrollTop=0; }}/> :tab==="reports"?p.pmId===user.id)} isAdmin={isAdmin} user={user}/> : }
{!selId&&
{navTabs.map(t=>)}
}
; } /* ── Project Row ── */ function ProjectRow({p,onSelect,isAdmin,onSave,allProjects}){ const pmts=p.payments||[]; const totalPd=p.paidInFull?(p.value||0):pmts.reduce((s,x)=>s+x.amount,0); const bal=p.paidInFull?0:(p.value||0)-totalPd; const pmUser=USERS.find(u=>u.id===p.pmId); const pct=p.completion||0; const col=pct===100?GR:pct>=60?AC:BL; const base=projBase(p), cost=projCost(p), gp=base-cost; const gpPct=base?(gp/base)*100:0; const pmCut=(gp*(p.pmPct||0))/100; const isFinished=p.status==="completed"&&p.paidInFull; return
onSelect(p.id)} style={{background:W,border:`1px solid ${BD}`,borderRadius:14,padding:"14px 16px",marginBottom:10,cursor:"pointer",transition:"box-shadow .15s"}} onMouseEnter={e=>e.currentTarget.style.boxShadow="0 4px 16px rgba(26,47,74,0.12)"} onMouseLeave={e=>e.currentTarget.style.boxShadow="none"}> {/* Slim read-only progress bar */}
Completion {pct}%
{p.name}
{isAdmin&&pmUser&&}
{isAdmin?( <>
{fmt(p.value||0)}
0?RE:MU,marginTop:2}}> {p.paidInFull?"✓ Paid in Full":bal>0?`${fmt(bal)} owed`:`${pmts.length} check${pmts.length!==1?"s":""}${pmts.length===0?" — unpaid":""}`}
):( isFinished?( <>
commission · =30?GR:gpPct>=15?AC:RE}}>{pctf(gpPct)} GP
{fmt(pmCut)}
):(
TBD
) )}
; } /* ── Projects Page ── */ function ProjectsPage({projects,allProjects,subCos,splits,user,isAdmin,onSave,onSelect}){ const[showNew,setShowNew]=useState(false); const srt=arr=>[...arr].sort((a,b)=>(b.completedDate||b.createdAt||"").localeCompare(a.completedDate||a.createdAt||"")); const running =[...projects.filter(p=>!(p.status==="completed"&&p.paidInFull))].sort((a,b)=>(b.completion||0)-(a.completion||0)); const needSplit=isAdmin?srt(projects.filter(p=>p.status==="completed"&&p.paidInFull&&!p.splitDone)):[]; const done =isAdmin?srt(projects.filter(p=>p.status==="completed"&&p.paidInFull&&p.splitDone)):srt(projects.filter(p=>p.status==="completed"&&p.paidInFull&&p.splitDone)); const awaitingComm=!isAdmin?srt(projects.filter(p=>p.status==="completed"&&p.paidInFull&&!p.splitDone)):[]; // Jobs with undeposited checks OR subs marked ready to pay but not yet paid const collectDeposit=isAdmin?srt(projects.filter(p=> !needSplit.find(n=>n.id===p.id) && ( (p.payments||[]).some(pmt=>!pmt.deposited) || (p.subs||[]).some(s=>s.readyToPay&&(!s.payStatus||s.payStatus==="unpaid"||s.payStatus==="partial")&&!isDebit(s)) ) )):[]; const rp={onSelect,isAdmin,onSave:v=>onSave(v),allProjects}; return
Projects
setShowNew(true)} color={AC} sm>+ New
{/* Admin action sections first */} {isAdmin&&collectDeposit.length>0&&
{collectDeposit.map(p=>{ const unhandled=(p.payments||[]).filter(pmt=>!pmt.givenToAdmin&&!pmt.deposited); const pendingDeposit=(p.payments||[]).filter(pmt=>pmt.givenToAdmin&&!pmt.deposited); const readySubs=(p.subs||[]).filter(s=>s.readyToPay&&(!s.payStatus||s.payStatus==="unpaid"||s.payStatus==="partial")&&!isDebit(s)); return
{unhandled.length>0&&
⚠ {unhandled.length} check{unhandled.length!==1?"s":""} not yet handed to you
} {pendingDeposit.length>0&&
📥 {pendingDeposit.length} check{pendingDeposit.length!==1?"s":""} received — deposit to bank
} {readySubs.length>0&&
⚠ {readySubs.length} sub{readySubs.length!==1?"s":""} ready to be paid
}
; })}
} {isAdmin&&needSplit.length>0&&
{needSplit.map(p=>)}
}
{running.length===0?:running.map(p=>)}
{!isAdmin&&awaitingComm.length>0&&
{awaitingComm.map(p=>)}
}
{done.length===0?:done.map(p=>)}
{showNew&&setShowNew(false)}> {onSave([...allProjects,{...p,id:uid(),subs:[],payments:[],createdAt:today(),status:"active",customerPaid:0,paidInFull:false,splitDone:false,completion:0,deposited:false}]);setShowNew(false);}}/> }
; } function Section({title,color,count,children}){ const countStr=count!=null?` · ${count} ${count===1?"project":"projects"}`:"" return
{title}{countStr}
{children}
; } /* ── New Project Form ── */ function NewProjForm({splits,user,isAdmin,onSubmit}){ const pms=USERS.filter(u=>u.role==="pm"); const defPmId=isAdmin?"":user.id; const defPmPct=isAdmin?0:getPmPct(user.username,false,splits); const[f,setF]=useState({name:"",state:"WA",taxCity:"Vancouver",taxRate:8.9,subTotal:"",pmId:defPmId,pmPct:defPmPct,attendedEst:false,marketingPct:splits.marketing||10,overheadPct:splits.overhead||5}); const set=(k,v)=>setF(p=>({...p,[k]:v})); const onPm=v=>{const u=USERS.find(x=>x.id===v);setF(p=>({...p,pmId:v,pmPct:getPmPct(u?.username||"",false,splits),attendedEst:false}));}; const onEst=v=>{const u=USERS.find(x=>x.id===f.pmId);setF(p=>({...p,attendedEst:v,pmPct:getPmPct(u?.username||"",v,splits)}));}; const onState=v=>setF(p=>({...p,state:v,taxCity:v==="OR"?"":p.taxCity||"Vancouver",taxRate:v==="OR"?0:rateForCity(p.taxCity||"Vancouver")})); const onCity=v=>setF(p=>({...p,taxCity:v,taxRate:rateForCity(v)})); const sub=+f.subTotal||0, tax=f.state==="WA"?sub*(+f.taxRate||0)/100:0, grand=sub+tax; const isCory=USERS.find(x=>x.id===f.pmId)?.username==="cory"; return
set("name",v)} placeholder="Smith Family — Master Bath"/> {f.state==="WA"&&} set("subTotal",v)} type="number" placeholder="0"/> {f.state==="WA"&&sub>0&&
Sub Total{fmt(sub)}
Tax ({f.taxRate}%){fmt(tax)}
Grand Total{fmt(grand)}
} {isAdmin&&<>({v:u.id,l:u.name}))]}/> {isCory&&
}} {/* Cory sees this on his own new projects */} {!isAdmin&&user.username==="cory"&&(
{setF(p=>({...p,attendedEst:v,pmPct:getPmPct("cory",v,splits)}));}} label={`I also estimated this project (+${splits.pm_cory_bonus||5}%)`} color={AC}/> {f.attendedEst&&
Your commission: {getPmPct("cory",true,splits)}% · Admin will verify before splitting.
}
)} {if(!f.name.trim()||!sub)return;onSubmit({...f,subTotal:sub,value:grand,customerTax:tax,pmPct:+f.pmPct||0,marketingPct:+f.marketingPct||0,overheadPct:+f.overheadPct||0,taxRate:+f.taxRate||0});}}>Create Project
; } /* ── Detail Page ── */ function DetailPage({project,projects,subCos,splits,user,isAdmin,onSave,onBack}){ const[showSubSheet,setShowSubSheet]=useState(false); const[editSub,setEditSub]=useState(null); const[showEdit,setShowEdit]=useState(false); const[showOverride,setShowOverride]=useState(false); const upd=u=>onSave(projects.map(p=>p.id===u.id?u:p)); // Weekly completion update gate — active jobs only const daysSinceUpdate = () => { if (!project.completionUpdatedAt) return 999; const then = new Date(project.completionUpdatedAt); const now = new Date(); return Math.floor((now - then) / (1000*60*60*24)); }; const isActive = project.status !== "completed"; const needsWeeklyUpdate = isActive && daysSinceUpdate() >= 1; const[weeklyDone, setWeeklyDone] = useState(!needsWeeklyUpdate); const[balanceWarning, setBalanceWarning] = useState(false); const onCompletionChange = v => { // Non-admin can't reach 100% while balance is still owed or checks not handed to admin const uncheckedChecks = pmts.filter(p => !p.givenToAdmin); const blocked = !isAdmin && v === 100 && (bal > 0 || uncheckedChecks.length > 0); if (blocked) { setBalanceWarning(true); upd({...project, completion:99, completionUpdatedAt:today()}); setWeeklyDone(true); return; } setBalanceWarning(false); const wasAutoCompleted = project.completion===100 && project.status==="completed"; const extra = v===100 ? {status:"completed", completedDate:project.completedDate||today()} : (wasAutoCompleted ? {status:"active", completedDate:null, paidInFull:false} : {}); upd({...project, completion:v, completionUpdatedAt:today(), ...extra}); setWeeklyDone(true); }; // Wrap all save actions to require weekly update first const guarded = fn => (...args) => { if (!weeklyDone) return; fn(...args); }; const onStatus=v=>upd({...project,status:v?"completed":"active",completedDate:v?today():null,...(v?{completion:100,completionUpdatedAt:today()}:{})}); const addPmt=amt=>{ const rate=project.state==="WA"?(+project.taxRate||0):0; const tax=rate>0?amt-amt/(1+rate/100):0; const newPmts=[...(project.payments||[]),{id:uid(),amount:amt,date:today(),tax}]; const newTotal=newPmts.reduce((s,p)=>s+p.amount,0); const nowFull=Math.abs(newTotal-(project.value||0))<0.02; upd({...project,payments:newPmts,customerPaid:newTotal,customerTax:newPmts.reduce((s,p)=>s+(p.tax||0),0), paidInFull:nowFull}); }; const delPmt=id=>{ const newPmts=(project.payments||[]).filter(p=>p.id!==id); const newTotal=newPmts.reduce((s,p)=>s+p.amount,0); const nowFull=newPmts.length>0&&Math.abs(newTotal-(project.value||0))<0.02; upd({...project,payments:newPmts,customerPaid:newTotal,customerTax:newPmts.reduce((s,p)=>s+(p.tax||0),0),paidInFull:nowFull}); }; const delSub=id=>upd({...project,subs:(project.subs||[]).filter(s=>s.id!==id)}); const addSubFn = s => { const entry = isDebit(s)?{...s,payStatus:"paid",readyToPay:false}:s; const u=editSub?{...project,subs:(project.subs||[]).map(x=>x.id===entry.id?entry:x)}:{...project,subs:[...(project.subs||[]),{...entry,id:uid()}]}; upd(u);setShowSubSheet(false);setEditSub(null); }; const subs=project.subs||[], pmts=project.payments||[]; const cost=projCost(project); const base=projBase(project); const gp=base-cost; const gpPct=base?(gp/base)*100:0; const pmCut=(gp*(project.pmPct||0))/100; const mktCut=(gp*(project.marketingPct||0))/100; const ovhCut=(gp*(project.overheadPct||0))/100; const netPaliy=gp-pmCut-mktCut-ovhCut; const pmUser=USERS.find(u=>u.id===project.pmId); const totalPaid=project.paidInFull?(project.value||0):pmts.reduce((s,p)=>s+p.amount,0); const bal=project.paidInFull?0:(project.value||0)-totalPaid; const taxPaidSubs=subs.reduce((s,x)=>s+(x.inclTax?((x.cost||0)*(x.taxRate||0))/100:0),0); return
{/* Header */}
{project.name}
setShowEdit(true)} color={N} sm outline>Edit
{/* Completion slider — always shown, gate unlock for active jobs */}
{needsWeeklyUpdate&&!weeklyDone&&(
Daily update required
Update completion % daily to unlock this job
)} 0||pmts.some(p=>!p.givenToAdmin)))?99:100}/> {project.completionUpdatedAt&&(
Last updated: {project.completionUpdatedAt}
)} {balanceWarning&&
⚠️
Can't mark 100% yet
{bal>0&&
Balance due: {fmt(bal)} — collect full payment first
} {pmts.some(p=>!p.givenToAdmin)&&
Check all payments as "given to admin" first
}
} {needsWeeklyUpdate&&!weeklyDone&&(
Move the slider above to unlock all other sections
)}
{/* Everything below locked until weekly update done */}
{/* Financials */}
Project Value
{fmt(project.value)}
Sub Costs
{fmt(cost)}
{!isAdmin&&gp!==0&&
=0?`${GR}10`:`${RE}10`,borderRadius:8,display:"flex",justifyContent:"space-between",alignItems:"center"}}> {project.status!=="completed"?"Current Gross Profit":"Final Gross Profit"} =0?GR:RE}}>{fmt(gp)} ({pctf(gpPct)})
} {isAdmin&&<>
{project.status!=="completed"?"Current Gross Profit":"Final Gross Profit"}
=0?GR:RE}}> {fmt(gp)} ({pctf(gpPct)})
{/* Admin % overrides — hidden behind toggle */}
setShowOverride(v=>!v)} style={{display:"flex",justifyContent:"space-between",alignItems:"center",cursor:"pointer"}}>
Override Splits
{showOverride&&
{[["PM %","pmPct"],["Mkt %","marketingPct"],["OH %","overheadPct"]].map(([lbl,key])=>(
{lbl}
upd({...project,[key]:+e.target.value})} onWheel={e=>e.target.blur()} style={{width:"100%",padding:"6px 8px",borderRadius:7,border:`1px solid ${BD}`,fontFamily:"inherit",fontSize:13,fontWeight:700,color:N,background:W}}/>
))}
}
{(()=>{ const taxCollected=project.paidInFull?(project.customerTax||0):pmts.reduce((s,p)=>s+(p.tax||0),0); const unpaidSubs=(project.subs||[]).filter(s=>!s.payStatus||s.payStatus==="unpaid"||s.payStatus==="partial"); const subsAllPaid=unpaidSubs.length===0; const readyToSplit=project.paidInFull&&subsAllPaid; return <> {readyToSplit ? <>
{pmUser&&}
Paliy Net {fmt(netPaliy)}
{project.state==="WA"&&<>
Sales Tax
Transfer to Tax Account {fmt(taxCollected-taxPaidSubs)}
} :
Final split amounts will show once job is paid in full and all subs are marked paid
{project.state==="WA"&&(()=>{const tc=pmts.reduce((s,p)=>s+(p.tax||0),0);return tc>0?
Tax collected so far {fmt(tc)}
:null;})()}
} ; })()} } {/* Split Done gate — admin only */} {isAdmin&&project.status==="completed"&&project.paidInFull&&(()=>{ const unpaidSubs=(project.subs||[]).filter(s=>!s.payStatus||s.payStatus==="unpaid"||s.payStatus==="partial"); const subsAllPaid=unpaidSubs.length===0; const allDeposited=pmts.length===0||pmts.every(p=>p.deposited); const readyToSplit=subsAllPaid&&allDeposited; return <> {!subsAllPaid&&(
⚠ Cannot split yet
{unpaidSubs.length} sub/material{unpaidSubs.length!==1?"s":""} still unpaid or partial:
{unpaidSubs.map(s=>
· {s.nickname?`${s.company} (${s.nickname})`:s.company} — {s.payStatus==="partial"?"Partial":"Unpaid"}
)}
)} {!allDeposited&&pmts.length>0&&(
⚠ Mark all checks as deposited first
{pmts.filter(p=>!p.deposited).length} check{pmts.filter(p=>!p.deposited).length!==1?"s":""} not yet deposited
)}
{pmUser?.username==="cory"&&project.attendedEst&&(
⚠ Verify estimate with manager
Cory claimed he attended the estimate on this job (+{splits.pm_cory_bonus||5}% bonus = {project.pmPct||0}% total). Please confirm with the manager before splitting.
)}
Job Split Completed upd({...project,splitDone:v})} color={BL}/>
; })()} {!isAdmin&&pmUser&&<> } {/* Project Completed toggle — PMs only, admin doesn't need it */} {!isAdmin&& {(()=>{ const noSubs=subs.length===0; const pmBlocked=(bal>0||noSubs)&&project.status!=="completed"; return <>
!pmBlocked&&onStatus(project.status!=="completed")}>
Project Completed
!pmBlocked&&onStatus(v)} color={BL}/>
{pmBlocked&&
{bal>0&&
Balance must be $0 before marking completed
} {noSubs&&
At least 1 sub or material must be added first
}
} {project.completedDate&&
Completed: {project.completedDate}
} ; })()}
} {/* Customer Payments */}
Customer Payments
{project.paidInFull&&✓ Paid in Full}
{/* Checks list — always visible */} {pmts.length>0&&
{pmts.map((pmt,i)=>{ const showTax=project.state==="WA"&&(pmt.tax||0)>0; const isDeposited=pmt.deposited||false; const isGiven=pmt.givenToAdmin||false; const toggleDeposit=()=>{const u=pmts.map(p=>p.id===pmt.id?{...p,deposited:!p.deposited}:p);upd({...project,payments:u});}; const toggleGiven=()=>{const u=pmts.map(p=>p.id===pmt.id?{...p,givenToAdmin:!p.givenToAdmin}:p);upd({...project,payments:u});}; return
Check #{i+1} · {pmt.date}
{showTax&&
Pre-tax: {fmt(pmt.amount-(pmt.tax||0))} · Tax: {fmt(pmt.tax||0)}
} {isAdmin&&
{isDeposited&&}
{isDeposited?"Deposited":"Mark as deposited"}
} {!isAdmin&&
{isGiven&&}
{isGiven?"Check given to admin":"Check given to admin?"}
}
{fmt(pmt.amount)} {(!project.paidInFull&&(isAdmin||!isDeposited))&&}
; })}
} {/* Add check — only when not fully paid */} {!project.paidInFull&&} {/* Running total */}
Total Received{fmt(totalPaid)}
{!project.paidInFull&&
Balance Remaining0?RE:GR}}>{fmt(bal)}
}
{/* Subs */}
Subcontractors / Materials
{setEditSub(null);setShowSubSheet(true);}}>+ Add
{subs.length===0?:
{subs.map((s,i)=>{ const stax=s.inclTax?(s.cost*(s.taxRate||0))/100:0; const stot=(s.cost||0)+stax; const dn=s.nickname?`${s.company} (${s.nickname})`:s.company; const toggleReady=()=>{ const updated=subs.map(x=>x.id===s.id?{...x,readyToPay:!x.readyToPay}:x); upd({...project,subs:updated}); }; return
{dn}
{s.desc&&
{s.desc}
}
{isAdmin?( <> {s.payStatus==="paid"&&✓ Paid in Full} {s.payStatus==="partial"&&Partial — {fmt(s.paidAmt)}} {(!s.payStatus||s.payStatus==="unpaid")&&Unpaid} {s.readyToPay&&s.payStatus!=="paid"&&· PM marked ready} ):( isDebit(s) ? Card transaction : s.payStatus==="paid" ? ✓ Paid in Full :
{s.readyToPay&&}
{s.readyToPay?"Sub Ready to be Paid in full!":"Sub Ready to be Paid in full?"}
)}
{fmt(stot)}
{stax>0&&
{fmt(s.cost)} + {fmt(stax)} ({s.taxRate}%) tax
}
{!isDebit(s)&&(isAdmin||s.payStatus!=="paid")&&}
; })}
Total {fmt(cost)}
}
{/* end weekly gate wrapper */} {showSubSheet&&{setShowSubSheet(false);setEditSub(null);}}> { const u=editSub?{...project,subs:subs.map(x=>x.id===s.id?s:x)}:{...project,subs:[...subs,{...s,id:uid()}]}; upd(u);setShowSubSheet(false);setEditSub(null); }}/> } {showEdit&&setShowEdit(false)}> {upd({...project,...p});setShowEdit(false);}} onDelete={()=>{onSave(projects.filter(x=>x.id!==project.id));onBack();}}/> }
; } /* ── Add Check Row ── */ function AddCheckRow({state,taxRate,onAdd}){ const[amt,setAmt]=useState(""); const isWA=state==="WA"&&+taxRate>0; const val=+amt||0, tax=isWA?val-val/(1+(+taxRate||0)/100):0, pre=val-tax; const submit=()=>{if(!val)return;onAdd(val);setAmt("");}; return
{isWA?`Add Check (includes ${taxRate}% tax)`:"Add Check Amount ($)"}
setAmt(e.target.value)} onWheel={e=>e.target.blur()} placeholder="0" style={{flex:1,padding:"11px 14px",background:CR,border:`1.5px solid ${BD}`,borderRadius:10,fontFamily:"inherit",fontSize:15,color:N,outline:"none",boxSizing:"border-box"}} onFocus={e=>e.target.style.borderColor=AC} onBlur={e=>e.target.style.borderColor=BD} onKeyDown={e=>e.key==="Enter"&&submit()}/>
{isWA&&val>0&&
Pre-tax: {fmt(pre)} Tax: {fmt(tax)}
}
; } /* ── Sub Form ── */ function SubForm({initial,subCos,projectState,defaultTax,isAdmin,onSubmit}){ const opts=subCos.map((s,i)=>{ const co=typeof s==="string"?s:(s.company||""); const nk=typeof s==="object"?(s.nickname||""):""; return{key:i,company:co,nickname:nk,label:nk?`${co} — ${nk}`:co}; }).filter(o=>o.company); const initIdx=initial?opts.findIndex(o=>o.company===initial.company):-1; const[selIdx,setSelIdx]=useState(initIdx>=0?String(initIdx):""); const[f,setF]=useState(initial||{company:"",nickname:"",desc:"",cost:"",inclTax:projectState==="WA",taxRate:defaultTax||8.7,payStatus:"unpaid",paidAmt:""}); const set=(k,v)=>setF(p=>({...p,[k]:v})); const onSel=idx=>{setSelIdx(idx);if(idx===""){setF(p=>({...p,company:"",nickname:""}));return;}const o=opts[+idx];if(o)setF(p=>({...p,company:o.company,nickname:o.nickname}));}; const tax=f.inclTax?((+f.cost||0)*(+f.taxRate||0))/100:0, tot=(+f.cost||0)+tax; return
{opts.length===0?
No subcontractors saved yet. Add them in Settings first.
:}
set("desc",v)} placeholder="Re-piping, tile, demo…"/> set("cost",v)} type="number" placeholder="0"/> {projectState==="WA"&&
set("inclTax",v)} label="Include Sales Tax"/> {f.inclTax&&
set("taxCity",v)&&set("taxRate",rateForCity(v))||setF(p=>({...p,taxCity:v,taxRate:rateForCity(v)}))} options={cityOpts} mb={0}/>
}
} {!!f.cost&&
Total {fmt(tot)}
} {isAdmin&&
{[["unpaid","Unpaid",RE],["paid","Paid",GR],["partial","Partial",AC]].map(([s,l,c])=>( ))}
} {isAdmin&&f.payStatus==="partial"&&set("paidAmt",v)} type="number" placeholder="0"/>} {if(!f.company||!f.cost)return;onSubmit({...f,cost:+f.cost||0,taxRate:+f.taxRate||0,paidAmt:+f.paidAmt||0});}}>Save
; } /* ── Edit Project Form ── */ function EditProjForm({project,splits,isAdmin,onSubmit,onDelete}){ const pms=USERS.filter(u=>u.role==="pm"); const[confirmDel,setConfirmDel]=useState(false); const stored=project.subTotal?project.subTotal:(project.state==="WA"&&project.taxRate?(project.value||0)/(1+(+project.taxRate||0)/100):(project.value||0)); const defCity=project.taxCity||(WA_CITIES.find(c=>c.rate===(project.taxRate||8.9))?.city)||"Vancouver"; const[f,setF]=useState({name:project.name||"",state:project.state||"WA",taxCity:defCity,taxRate:project.taxRate||8.9,subTotal:String(Math.round(stored*100)/100||""),pmId:project.pmId||"",pmPct:project.pmPct||0,attendedEst:project.attendedEst||false,marketingPct:project.marketingPct||splits.marketing,overheadPct:project.overheadPct||splits.overhead}); const set=(k,v)=>setF(p=>({...p,[k]:v})); const onPm=v=>{const u=USERS.find(x=>x.id===v);setF(p=>({...p,pmId:v,pmPct:getPmPct(u?.username||"",false,splits),attendedEst:false}));}; const onEst=v=>{const u=USERS.find(x=>x.id===f.pmId);setF(p=>({...p,attendedEst:v,pmPct:getPmPct(u?.username||"",v,splits)}));}; const onState=v=>setF(p=>({...p,state:v,taxCity:v==="OR"?"":p.taxCity||"Vancouver",taxRate:v==="OR"?0:rateForCity(p.taxCity||"Vancouver")})); const onCity=v=>setF(p=>({...p,taxCity:v,taxRate:rateForCity(v)})); const isCory=USERS.find(x=>x.id===f.pmId)?.username==="cory"; const sub=+f.subTotal||0, tax=f.state==="WA"?sub*(+f.taxRate||0)/100:0, grand=sub+tax; return
set("name",v)}/> {f.state==="WA"&&} set("subTotal",v)} type="number"/> {f.state==="WA"&&sub>0&&
Sub Total{fmt(sub)}
Tax ({f.taxRate}%){fmt(tax)}
Grand Total{fmt(grand)}
} {isAdmin&&<>({v:u.id,l:u.name}))]}/> {isCory&&
}
Profit Split Override
set("pmPct",+v)} type="number" mb={0}/> set("marketingPct",+v)} type="number" mb={0}/> set("overheadPct",+v)} type="number" mb={0}/>
} {if(!f.name.trim())return;onSubmit({...f,subTotal:sub,value:grand,customerTax:tax,pmPct:+f.pmPct||0,marketingPct:+f.marketingPct||0,overheadPct:+f.overheadPct||0,taxRate:+f.taxRate||0});}}>Save Changes
{(()=>{ const hasLocked=!isAdmin&&( (project.payments||[]).some(p=>p.deposited)|| (project.subs||[]).some(s=>s.payStatus==="paid") ); if(hasLocked) return
Job cannot be deleted — admin has confirmed payments or paid subs
; return (isAdmin||project.status!=="completed")&&(!confirmDel ?setConfirmDel(true)}>Delete Project :
Delete this project?
setConfirmDel(false)}>Cancel Yes, Delete
); })()}
; } /* ── Reports ── */ const MONTHS=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]; function ReportsPage({projects, isAdmin, user}){ const allYears=[...new Set(projects.map(p=>(p.completedDate||p.createdAt||"").slice(0,4)).filter(Boolean))].sort().reverse(); const[yr,setYr]=useState(allYears[0]||"all"); const[mo,setMo]=useState("all"); const[st,setSt]=useState("both"); const[jobStatus,setJobStatus]=useState("done"); // admin only: done | split | ongoing const onYr=v=>{setYr(v);setMo("all");}; const base_qual = isAdmin ? projects.filter(p=>{ if(jobStatus==="done") return p.status==="completed"&&p.paidInFull&&p.splitDone; if(jobStatus==="split") return p.status==="completed"&&p.paidInFull&&!p.splitDone; if(jobStatus==="ongoing") return !(p.status==="completed"&&p.paidInFull); return p.status==="completed"&&p.paidInFull; }) : projects.filter(p=>p.status==="completed"&&p.paidInFull); const fil=base_qual.filter(p=>{ const d=p.completedDate||p.createdAt||""; if(yr!=="all"&&!d.startsWith(yr))return false; if(mo!=="all"&&d.slice(5,7)!==mo)return false; if(st==="WA"&&p.state==="OR")return false; if(st==="OR"&&(p.state||"WA")!=="OR")return false; return true; }); const rev=fil.reduce((s,p)=>s+(p.value||0),0); const preRev=fil.reduce((s,p)=>s+projBase(p),0); const cost=fil.reduce((s,p)=>s+projCost(p),0); const gp=preRev-cost; const gpP=preRev?(gp/preRev)*100:0; const taxCol=fil.reduce((s,p)=>s+(p.customerTax||0),0); const taxSubs=fil.reduce((s,p)=>s+(p.subs||[]).reduce((t,x)=>t+(x.inclTax?((x.cost||0)*(x.taxRate||0))/100:0),0),0); const collected=fil.reduce((s,p)=>s+(p.paidInFull?(p.value||0):(p.customerPaid||0)),0); const outstanding=projects.filter(p=>!p.paidInFull).reduce((s,p)=>s+((p.value||0)-(p.customerPaid||0)),0); const waCount=fil.filter(p=>(p.state||"WA")==="WA").length; const orCount=fil.filter(p=>p.state==="OR").length; const waRev=fil.filter(p=>(p.state||"WA")==="WA").reduce((s,p)=>s+(p.value||0),0); const orRev=fil.filter(p=>p.state==="OR").reduce((s,p)=>s+(p.value||0),0); const mktTotal=fil.reduce((s,p)=>{ const g=projBase(p)-projCost(p); return s+(g*(p.marketingPct||0))/100; },0); const ovhTotal=fil.reduce((s,p)=>{ const g=projBase(p)-projCost(p); return s+(g*(p.overheadPct||0))/100; },0); const pmTotal =fil.reduce((s,p)=>{ const g=projBase(p)-projCost(p); return s+(g*(p.pmPct||0))/100; },0); const paliyNet=gp-pmTotal-mktTotal-ovhTotal; const activeMos=[...new Set( base_qual .filter(p=>yr==="all"||(p.completedDate||p.createdAt||"").startsWith(yr)) .map(p=>(p.completedDate||p.createdAt||"").slice(5,7)) .filter(Boolean) )].sort(); const bySub={}; fil.forEach(p=>(p.subs||[]).forEach(s=>{const k=s.nickname?`${s.company} (${s.nickname})`:(s.company||"Unknown");bySub[k]=(bySub[k]||0)+subTot(s);})); const Stat=({l,v,c=N})=>
{l}
{v}
; return
Reports
{/* Admin job status filter */} {isAdmin&&
Job Status
{[["done","Completed"],["split","Need to Split"],["ongoing","Ongoing"]].map(([v,l])=>( ))}
} {/* Year */}
Year
{["all",...allYears].map(y=>)}
Month
{MONTHS.map((m,i)=>{const k=String(i+1).padStart(2,"0"),has=activeMos.includes(k);return ;})}
State
{[["both","Both States"],["WA","Washington"],["OR","Oregon"]].map(([v,l])=>)}
Showing: {[yr==="all"?"All Time":yr,mo!=="all"?MONTHS[+mo-1]:null,st!=="both"?st:null].filter(Boolean).join(" · ")} — {fil.length} job{fil.length!==1?"s":""}
{/* PM simplified stats */} {!isAdmin&&<>
=30?GR:gpP>=15?AC:RE}/>
} {/* Admin full stats */} {isAdmin&&<>
=30?GR:gpP>=15?AC:RE}/>
0?RE:GR}/>
Paliy Net
After PM cuts, marketing & overhead
{fmt(paliyNet)}
}
Spending by Category
{Object.keys(bySub).length===0?
No data.
:Object.entries(bySub).sort((a,b)=>b[1]-a[1]).map(([co,amt])=>
{co}
{fmt(amt)}
)}
{/* Job Status list — 3 sections for admin, simple for PM */} {(()=>{ const allProjects = projects; // full unfiltered list for status grouping // For admin: show 3 groups across ALL projects (not just filtered by year/month) // For PM: show their completed jobs if(isAdmin){ const pending = allProjects.filter(p=>p.status==="completed"&&!p.paidInFull); const needSplit = allProjects.filter(p=>p.status==="completed"&&p.paidInFull&&!p.splitDone); const completed = allProjects.filter(p=>p.status==="completed"&&p.paidInFull&&p.splitDone); const JobRow = ({p})=>{ const pg=projBase(p)-projCost(p), pp=projBase(p)?(pg/projBase(p))*100:0, pm=USERS.find(u=>u.id===p.pmId); return
{p.name}
{pm&&{pm.name}} {p.completedDate&&{p.completedDate}}
=0?GR:RE}}>{fmt(pg)}
{pctf(pp)}
; }; const Section3 = ({title,color,jobs})=>
{title}
{jobs.length} job{jobs.length!==1?"s":""}
{jobs.length===0?
None
:jobs.sort((a,b)=>(b.completedDate||"").localeCompare(a.completedDate||"")).map(p=>)}
; return <> ; } // PM view — just their completed jobs return
Completed Jobs — {fil.length}
{fil.length===0?
No completed jobs.
:[...fil].sort((a,b)=>(b.completedDate||"").localeCompare(a.completedDate||"")).map(p=>{ const pg=projBase(p)-projCost(p), pp=projBase(p)?(pg/projBase(p))*100:0; const pmCut=(pg*(p.pmPct||0))/100; return
{p.name}
{p.completedDate&&{p.completedDate}}
{fmt(pmCut)}
{pctf(pp)} GP
; })}
; })()}
; } /* ── Settings ── */ function SettingsPage({subCos,splits,isAdmin,onSaveSubs,onSaveSplits}){ const[tab,setTab]=useState("subs"); const[coName,setCoName]=useState(""), [coNick,setCoNick]=useState(""); const[sp,setSp]=useState({...DEF_SPLITS,...splits}); const addSub=()=>{if(!coName.trim())return;onSaveSubs([...subCos,{company:coName.trim(),nickname:coNick.trim()}]);setCoName("");setCoNick("");}; const saveSp=()=>{const u={marketing:+sp.marketing||0,overhead:+sp.overhead||0,pm_ivan:+sp.pm_ivan||25,pm_cory:+sp.pm_cory||20,pm_cory_bonus:+sp.pm_cory_bonus||5,pm_yuriy:+sp.pm_yuriy||25,pm_mark:+sp.pm_mark||25};onSaveSplits(u);alert("Saved.");}; const tabs=isAdmin?[["subs","Subcontractors"],["splits","Splits"]]:[["subs","Subcontractors"]]; return
Settings
{tabs.map(([id,l])=>)}
{tab==="subs"&&
Add Subcontractor / Material
Add
{subCos.length===0? :
{subCos.map((c,i)=>{ const name=typeof c==="string"?c:(c.company||""), nick=typeof c==="object"?(c.nickname||""):""; return
{name}
{nick&&
{nick}
}
; })}
}
} {tab==="splits"&&
Company Splits
setSp(s=>({...s,marketing:v}))} type="number" mb={0}/> setSp(s=>({...s,overhead:v}))} type="number" mb={0}/>
PM Splits
{[{k:"pm_ivan",l:"Ivan %"},{k:"pm_yuriy",l:"Yuriy %"},{k:"pm_mark",l:"Mark %"},{k:"pm_cory",l:"Cory % (base)"},{k:"pm_cory_bonus",l:"Cory % (estimate bonus)"}].map(({k,l})=>( setSp(s=>({...s,[k]:v}))} type="number" mb={0}/> ))}
Save All Splits
}
; } /* ── Mount the app ── */ const _root = ReactDOM.createRoot(document.getElementById('root')); _root.render(React.createElement(App));