PageSourceSearch

https://budget.schoolstack.ai/assets/index-DY1q_cvA.js

js schoolstack.ai collected 2026-09-28 12:09:04 UTC 15,539 bytes, 1 lines download raw bytes

1import{a as u,j as e}from"./vendor-query-DqBOI2Qr.js";import{y as te,j as se,o as ae,x as ne,L as re,n as E}from"./index-2Iygp1_T.js";import{L as W}from"./Layout-B5LWFSg6.js";import{m as le}from"./model-migrations-CMyRYieN.js";import"./wizard-step-layout-kPbOpUfQ.js";import{b as oe,a as de,c as $,e as F,C as P}from"./variance-C_zYi_Ht.js";import{L as Y,am as ce,aB as ie,G as me,ab as ue,U as xe,f as ge,aC as he,W as pe,aD as be,w as fe,q as je,v as ye,C as Ne,n as ve,ax as Ce}from"./vendor-icons-BZhNkIu0.js";import"./vendor-radix-ClRA1DoJ.js";import"./vendor-cn-Ce1ZTWB2.js";import"./track-DwxtzGjD.js";import"./scenario-engine-D3Jc7xth.js";import"./constants-Be0ioUE7.js";import"./coaching-flag-guardrail-Bu1nWbD0.js";import"./diagnostics-engine-C1i6Yvub.js";import"./schema-BaniF-L7.js";import"./vendor-zod-D-s0kEgb.js";import"./program-overlap-insights-BuFXkCzF.js";import"./reserve-banking-BMA29MUQ.js";const O=P[0]?.typicalLow,L=P[0]?.floor;function Ae(n){return n>=O?"green":n>=L?"yellow":"red"}const we={green:{dot:"bg-emerald-500",text:"text-emerald-700",label:"On track"},yellow:{dot:"bg-amber-500",text:"text-amber-700",label:"Watch"},red:{dot:"bg-rose-500",text:"text-rose-700",label:"Off track"}};function R({status:n}){const r=we[n],d=n==="green"?Ne:n==="yellow"?ve:Ce;return e.jsxs("span",{className:`inline-flex items-center gap-1.5 text-sm font-medium ${r.text}`,children:[e.jsx(d,{className:"h-4 w-4"}),r.label]})}function K({amount:n}){return Math.abs(n)<.5?e.jsx(fe,{className:"h-4 w-4 text-muted-foreground"}):n>0?e.jsx(je,{className:"h-4 w-4 text-emerald-600"}):e.jsx(ye,{className:"h-4 w-4 text-rose-600"})}function B({value:n,onChange:r,prefix:d,ariaLabel:g}){return e.jsxs("div",{className:"relative",children:[d&&e.jsx("span",{className:"pointer-events-none absolute left-2.5 top-1/2 -translate-y-1/2 text-sm text-muted-foreground",children:d}),e.jsx("input",{type:"number",inputMode:"decimal","aria-label":g,value:Number.isFinite(n)&&n!==0?n:"",placeholder:"0",onChange:p=>{const m=p.target.value===""?0:Number(p.target.value);r(Number.isFinite(m)?m:0)},className:`w-full rounded-lg border border-border bg-background py-2 text-right text-sm focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary ${d?"pl-7 pr-2.5":"px-2.5"}`})]})}function S({title:n,icon:r,kind:d,rows:g,onActualChange:p,intro:m}){return g.length===0?null:e.jsxs("section",{className:"rounded-2xl border border-border/60 bg-card p-5 sm:p-6 shadow-sm",children:[e.jsxs("div",{className:"mb-4 flex items-center gap-2.5",children:[e.jsx("div",{className:"flex h-9 w-9 items-center justify-center rounded-xl bg-primary/10 text-primary",children:r}),e.jsx("h2",{className:"font-display text-lg font-bold text-foreground",children:n})]}),m&&e.jsx("p",{className:"mb-4 text-base text-muted-foreground",children:m}),e.jsx("div",{className:"space-y-4",children:g.map(s=>{const f=$(d,s.budgeted,s.actual),h=s.actual-s.budgeted,c=s.budgeted>0?h/s.budgeted*100:0,A=F(d,s.label,s.budgeted,s.actual);return e.jsxs("div",{className:"rounded-xl border border-border/50 bg-background/40 p-4","data-testid":`actuals-row-${d}-${s.key}`,children:[e.jsxs("div",{className:"grid grid-cols-2 gap-3 sm:grid-cols-12 sm:items-center",children:[e.jsx("div",{className:"sm:col-span-3",children:e.jsx("p",{className:"text-sm font-semibold text-foreground",children:s.label})}),e.jsxs("div",{className:"sm:col-span-2",children:[e.jsx("p",{className:"text-xs text-muted-foreground",children:"Budgeted"}),e.jsx("p",{className:"text-sm font-medium tabular-nums text-foreground",children:E(s.budgeted)})]}),e.jsxs("div",{className:"sm:col-span-3",children:[e.jsx("p",{className:"mb-1 text-xs text-muted-foreground",children:"Actual"}),e.jsx(B,{value:s.actual,prefix:"$",ariaLabel:`${n} ${s.label} actual`,onChange:l=>p(s.key,l)})]}),e.jsxs("div",{className:"sm:col-span-2",children:[e.jsx("p",{className:"text-xs text-muted-foreground",children:"Variance"}),e.jsxs("p",{className:"flex items-center gap-1.5 text-sm font-medium tabular-nums text-foreground",children:[e.jsx(K,{amount:h}),E(h),s.budgeted>0&&e.jsxs("span",{className:"text-xs text-muted-foreground",children:["(",c>0?"+":"",Math.round(c),"%)"]})]})]}),e.jsx("div",{className:"sm:col-span-2",children:e.jsx(R,{status:f})})]}),e.jsx("p",{className:"mt-2.5 text-sm text-muted-foreground",children:A})]},s.key)})})]})}function _(){return{enrollment:{},revenue:{},staffing:{},facility:{},operating:{},cash:{beginningCash:0,cashIn:0,cashOut:0,endingCash:0},notes:""}}function Ie(n,r){const d=_(),g=p=>{const m={};
1for(const s of p??[])m[s.category]=s.actualAmount;return m};if(r){for(const s of r.enrollmentActuals??[])d.enrollment[s.programId]=s.actualEnrollment;const p=g(r.revenueActuals);d.revenue=p;const m=g(r.expenseActuals);for(const s of n.staffing)d.staffing[s.category]=m[s.category]??0;for(const s of n.facility)d.facility[s.category]=m[s.category]??0;for(const s of n.operating)d.operating[s.category]=m[s.category]??0;d.cash={beginningCash:r.beginningCash??n.cash.beginningCash,cashIn:r.cashIn??0,cashOut:r.cashOut??0,endingCash:r.endingCash??0},d.notes=r.notes??""}else d.cash.beginningCash=n.cash.beginningCash;return d}function Ve(){const[,n]=te("/model/:id/actuals"),r=n?.id?Number(n.id):0,{toast:d}=se(),{data:g,isLoading:p}=ae(r||0,{query:{queryKey:[`/api/models/${r||0}`],enabled:!!r}}),m=ne(),s=u.useMemo(()=>g?.data?le(g.data):null,[g?.data]),f=u.useMemo(()=>s?oe(s):[],[s]),h=u.useMemo(()=>s?.monthlyActuals??[],[s]),[c,A]=u.useState(0),[l,N]=u.useState(_()),M=u.useRef("");u.useEffect(()=>{if(h.length===0)return;const t=h.reduce((a,i)=>(i.monthIndex??0)>(a.monthIndex??0)?i:a);A(t.monthIndex??0)},[h]);const o=u.useMemo(()=>s?de(s,c):null,[s,c]),v=u.useMemo(()=>h.find(t=>(t.monthIndex??0)===c),[h,c]);u.useEffect(()=>{if(!o)return;const t=`${r}:${c}:${g?.updatedAt??""}`;M.current!==t&&(M.current=t,N(Ie(o,v)))},[o,v,r,c,g?.updatedAt]);const D=u.useCallback((t,a)=>{N(i=>({...i,enrollment:{...i.enrollment,[t]:a}}))},[]),w=u.useCallback((t,a,i)=>{N(x=>({...x,[t]:{...x[t],[a]:i}}))},[]),V=u.useCallback((t,a)=>{N(i=>({...i,cash:{...i.cash,[t]:a}}))},[]),T=u.useCallback(()=>{if(!o)return null;const t=new Date().toISOString(),a=f[c],i=o.enrollment.map(y=>({programId:y.programId,programName:y.programName,budgetedEnrollment:y.budgetedEnrollment,actualEnrollment:l.enrollment[y.programId]??0})),x=(y,Z,ee)=>y.map(b=>{const k=Z[b.category]??0;return{id:`${a?.monthKey??c}-${b.category}`,category:b.category,label:b.label,budgetedAmount:b.budgetedAmount,actualAmount:k,varianceAmount:k-b.budgetedAmount,variancePercent:b.budgetedAmount>0?(k-b.budgetedAmount)/b.budgetedAmount*100:0,status:$(ee,b.budgetedAmount,k)}}),j=x(o.revenue,l.revenue,"revenue"),C=[...x(o.staffing,l.staffing,"expense"),...x(o.facility,l.facility,"expense"),...x(o.operating,l.operating,"expense")];return{id:v?.id??`actuals-${a?.monthKey??c}`,month:a?.monthKey??String(c),fiscalYear:"Year 1",monthIndex:c,enrollmentActuals:i,revenueActuals:j,expenseActuals:C,beginningCash:l.cash.beginningCash,cashIn:l.cash.cashIn,cashOut:l.cash.cashOut,endingCash:l.cash.endingCash,budgetedBeginningCash:o.cash.beginningCash,budgetedCashIn:o.cash.cashIn,budgetedCashOut:o.cash.cashOut,budgetedEndingCash:o.cash.endingCash,notes:l.notes,createdAt:v?.createdAt??t,updatedAt:t}},[o,f,c,l,v]),q=u.useCallback(()=>{if(!s||!r)return;const t=T();if(!t)return;const i=[...h.filter(j=>(j.monthIndex??0)!==c),t].sort((j,C)=>(j.monthIndex??0)-(C.monthIndex??0)),x={...s,monthlyActuals:i};delete x.assumptionFlags,m.mutate({id:r,data:{data:x}},{onSuccess:()=>{M.current="",d({title:"Actuals saved",description:`${f[c]?.label??"This month"} actuals were saved to your model.`})},onError:()=>{d({title:"Could not save",description:"Something went wrong saving your actuals. Please try again.",variant:"destructive"})}})},[s,r,T,h,c,m,d,f]);if(p||!s||!o)return e.jsx(W,{children:e.jsx("div",{className:"flex min-h-[60vh] items-center justify-center",children:e.jsx(Y,{className:"h-8 w-8 animate-spin text-primary"})})});const G=o.enrollment,H=o.revenue.map(t=>({key:t.category,label:t.label,budgeted:t.budgetedAmount,actual:l.revenue[t.category]??0})),X=o.staffing.map(t=>({key:t.category,label:t.label,budgeted:t.budgetedAmount,actual:l.staffing[t.category]??0})),z=o.facility.map(t=>({key:t.category,label:t.label,budgeted:t.budgetedAmount,actual:l.facility[t.category]??0})),J=o.operating.map(t=>({key:t.category,label:t.label,budgeted:t.budgetedAmount,actual:l.operating[t.category]??0})),I=Object.values(l.staffing).reduce((t,a)=>t+a,0)+Object.values(l.facility).reduce((t,a)=>t+a,0)+Object.values(l.operating).reduce((t,a)=>t+a,0),U=I>0?l.cash.endingCash/I:0,Q=Ae(U);return e.jsx(W,{children:e.jsxs("div",{className:"mx-auto max-w-5xl px-4 py-8 sm:px-6",children:[e.jsx(re,{href:`/model/${r}`,children:e.jsxs("a",{className:"mb-4 inline-flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground",children:[e.jsx(ce,{className:"h-4 w-4"})," Back to model"]})}),e.jsxs("div",{className:"mb-6 flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"font-display text-2xl font-bold text-foreground sm:text-3xl",children:"Actuals vs Budget"}),e.jsx("p",{className:"mt-1 text-base text-muted-foreground",children:"Record what really happened each month and see how it compares to your plan, in plain English."})]}),e.jsxs("div",{className:"flex items-end gap-3",children:[e.jsxs("label",{className:"flex flex-col text-sm",children:[e.jsx("span",{className:"mb-1 font-medium text-foreground",children:"Month"}),e.jsx("select",{"aria-label":"Select month",value:c,onChange:t=>A(Number(t.target.value)),className:"rounded-lg border border-border bg-background px-3 py-2 text-sm focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary","data-testid":"actuals-month-select",children:f.map(t=>{const a=h.some(i=>(i.monthIndex??0)===t.monthIndex);return e.jsxs("option",{value:t.monthIndex,children:[t.label,a?" (saved)":""]},t.monthIndex)})})]}),e.jsxs("button",{onClick:q,disabled:m.isPending,className:"inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground hover:bg-primary/90 disabled:opacity-60","data-testid":"actuals-save-button",children:[m.isPending?e.jsx(Y,{className:"h-4 w-4 animate-spin"}):e.jsx(ie,{className:"h-4 w-4"}),"Save actuals"]})]})]}),e.jsxs("div",{className:"space-y-5",children:[e.jsxs("section",{className:"rounded-2xl border border-border/60 bg-card p-5 shadow-sm sm:p-6",children:[e.jsxs("div",{className:"mb-4 flex items-center gap-2.5",children:[e.jsx("div",{className:"flex h-9 w-9 items-center justify-center rounded-xl bg-primary/10 text-primary",children:e.jsx(me,{className:"h-5 w-5"})}),e.jsx("h2",{className:"font-display text-lg font-bold text-foreground",children:"Enrollment"})]}),e.jsx("div",{className:"space-y-4",children:G.map(t=>{const a=l.enrollment[t.programId]??0,i=$("enrollment",t.budgetedEnrollment,a),x=a-t.budgetedEnrollment,j=F("enrollment","Enrollment",t.budgetedEnrollment,a,{programName:t.programName});return e.jsxs("div",{className:"rounded-xl border border-border/50 bg-background/40 p-4","data-testid":`actuals-row-enrollment-${t.programId}`,children:[e.jsxs("div",{className:"grid grid-cols-2 gap-3 sm:grid-cols-12 sm:items-center",children:[e.jsx("div",{className:"sm:col-span-3",children:e.jsx("p",{className:"text-sm font-semibold text-foreground",children:t.programName})}),e.jsxs("div",{className:"sm:col-span-2",children:[e.jsx("p",{className:"text-xs text-muted-foreground",children:"Budgeted"}),e.jsx("p",{className:"text-sm font-medium tabular-nums text-foreground",children:Math.round(t.budgetedEnrollment)})]}),e.jsxs("div",{className:"sm:col-span-3",children:[e.jsx("p",{className:"mb-1 text-xs text-muted-foreground",children:"Actual"}),e.jsx(B,{value:a,ariaLabel:`${t.programName} actual enrollment`,onChange:C=>D(t.programId,C)})]}),e.jsxs("div",{className:"sm:col-span-2",children:[e.jsx("p",{className:"text-xs text-muted-foreground",children:"Variance"}),e.jsxs("p",{className:"flex items-center gap-1.5 text-sm font-medium tabular-nums text-foreground",children:[e.jsx(K,{amount:x}),x>0?"+":"",Math.round(x)]})]}),e.jsx("div",{className:"sm:col-span-2",children:e.jsx(R,{status:i})})]}),e.jsx("p",{className:"mt-2.5 text-sm text-muted-foreground",children:j})]},t.programId)})})]}),e.jsx(S,{title:"Revenue",icon:e.jsx(ue,{className:"h-5 w-5"}
1),kind:"revenue",rows:H,onActualChange:(t,a)=>w("revenue",t,a)}),e.jsx(S,{title:"Staffing / Personnel",icon:e.jsx(xe,{className:"h-5 w-5"}),kind:"expense",rows:X,onActualChange:(t,a)=>w("staffing",t,a)}),e.jsx(S,{title:"Facility",icon:e.jsx(ge,{className:"h-5 w-5"}),kind:"expense",rows:z,onActualChange:(t,a)=>w("facility",t,a)}),e.jsx(S,{title:"Operating Expense",icon:e.jsx(he,{className:"h-5 w-5"}),kind:"expense",rows:J,onActualChange:(t,a)=>w("operating",t,a)}),e.jsxs("section",{className:"rounded-2xl border border-border/60 bg-card p-5 shadow-sm sm:p-6",children:[e.jsxs("div",{className:"mb-4 flex items-center justify-between gap-2.5",children:[e.jsxs("div",{className:"flex items-center gap-2.5",children:[e.jsx("div",{className:"flex h-9 w-9 items-center justify-center rounded-xl bg-primary/10 text-primary",children:e.jsx(pe,{className:"h-5 w-5"})}),e.jsx("h2",{className:"font-display text-lg font-bold text-foreground",children:"Cash"})]}),e.jsxs("div",{className:"flex items-center gap-2 text-sm",children:[e.jsx("span",{className:"text-muted-foreground",children:"Runway"}),e.jsx("span",{className:"font-semibold tabular-nums text-foreground",children:I>0?`${U.toFixed(1)} mo`:"-"}),I>0&&e.jsx(R,{status:Q})]})]}),e.jsx("div",{className:"grid gap-4 sm:grid-cols-2 lg:grid-cols-4",children:[{field:"beginningCash",label:"Beginning cash",budget:o.cash.beginningCash},{field:"cashIn",label:"Cash in",budget:o.cash.cashIn},{field:"cashOut",label:"Cash out",budget:o.cash.cashOut},{field:"endingCash",label:"Ending cash",budget:o.cash.endingCash}].map(t=>e.jsxs("div",{className:"rounded-xl border border-border/50 bg-background/40 p-4","data-testid":`actuals-cash-${t.field}`,children:[e.jsx("p",{className:"text-sm font-semibold text-foreground",children:t.label}),e.jsxs("p",{className:"mb-2 text-xs text-muted-foreground",children:["Budgeted ",E(t.budget)]}),e.jsx(B,{value:l.cash[t.field],prefix:"$",ariaLabel:`${t.label} actual`,onChange:a=>V(t.field,a)})]},t.field))}),e.jsxs("p",{className:"mt-3 text-sm text-muted-foreground",children:["Runway uses your school's calibrated thresholds:"," ",O," months or more is healthy, ",L," to"," ",O," is worth watching, and under ",L," ","months is a warning."]})]}),e.jsxs("section",{className:"rounded-2xl border border-border/60 bg-card p-5 shadow-sm sm:p-6",children:[e.jsxs("div",{className:"mb-4 flex items-center gap-2.5",children:[e.jsx("div",{className:"flex h-9 w-9 items-center justify-center rounded-xl bg-primary/10 text-primary",children:e.jsx(be,{className:"h-5 w-5"})}),e.jsx("h2",{className:"font-display text-lg font-bold text-foreground",children:"Notes"})]}),e.jsx("textarea",{value:l.notes,onChange:t=>N(a=>({...a,notes:t.target.value})),rows:4,"aria-label":"Month notes",placeholder:"What drove this month's results? Anything the board should know?",className:"w-full rounded-lg border border-border bg-background px-3 py-2 text-sm focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary","data-testid":"actuals-notes"})]})]})]})})}export{Ve as ActualsPage};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.