Add PY wages/sales cards + dept % of total column
- Weekly/Monthly: PY Wages summary card (fetches equivalent week/month last year) - Weekly/Monthly: PY Net Sales as its own card (was a sub-label, now prominent) - Weekly/Monthly: dept table gains '% of Total' column - Monthly: PY comparison caps at same day-of-month for fair PYTD comparison Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
605feaab4e
commit
1d7cb229e4
2 changed files with 78 additions and 19 deletions
|
|
@ -27,6 +27,8 @@ export default function Monthly() {
|
||||||
const [depts, setDepts] = useState<DeptActuals[]>([])
|
const [depts, setDepts] = useState<DeptActuals[]>([])
|
||||||
const [scheduled, setScheduled] = useState<Record<string, Record<string, number>>>({})
|
const [scheduled, setScheduled] = useState<Record<string, Record<string, number>>>({})
|
||||||
const [netSales, setNetSales] = useState(0)
|
const [netSales, setNetSales] = useState(0)
|
||||||
|
const [pySales, setPySales] = useState(0)
|
||||||
|
const [pyWages, setPyWages] = useState<number | null>(null)
|
||||||
const [budget, setBudget] = useState<number | null>(null)
|
const [budget, setBudget] = useState<number | null>(null)
|
||||||
const [deptPcts, setDeptPcts] = useState<Record<string, number>>({})
|
const [deptPcts, setDeptPcts] = useState<Record<string, number>>({})
|
||||||
const [showOncosts, setShowOncosts] = useState(true)
|
const [showOncosts, setShowOncosts] = useState(true)
|
||||||
|
|
@ -38,19 +40,24 @@ export default function Monthly() {
|
||||||
const fromStr = `${monthStr}-01`
|
const fromStr = `${monthStr}-01`
|
||||||
const toStr = `${monthStr}-${String(dim).padStart(2, '0')}`
|
const toStr = `${monthStr}-${String(dim).padStart(2, '0')}`
|
||||||
const isCurrentMonth = year === todayYear && month === todayMonth
|
const isCurrentMonth = year === todayYear && month === todayMonth
|
||||||
// For net sales: cap at today for current month, use month-end for past months
|
const salesTo = isCurrentMonth ? todayStr : toStr
|
||||||
const salesTo = isCurrentMonth ? todayStr : toStr
|
|
||||||
// For scheduled: only relevant if month includes future dates
|
|
||||||
const schedFrom = todayStr < toStr ? todayStr : toStr
|
const schedFrom = todayStr < toStr ? todayStr : toStr
|
||||||
|
// Prior-year equivalent period
|
||||||
|
const pyDim = daysInMonth(year - 1, month)
|
||||||
|
const pyFromStr = `${year - 1}-${String(month).padStart(2, '0')}-01`
|
||||||
|
const pyToStr = isCurrentMonth
|
||||||
|
? `${year - 1}-${String(month).padStart(2, '0')}-${String(Math.min(parseInt(todayStr.slice(8)), pyDim)).padStart(2, '0')}`
|
||||||
|
: `${year - 1}-${String(month).padStart(2, '0')}-${String(pyDim).padStart(2, '0')}`
|
||||||
|
|
||||||
const load = useCallback(async () => {
|
const load = useCallback(async () => {
|
||||||
setLoading(true); setError(null)
|
setLoading(true); setError(null)
|
||||||
try {
|
try {
|
||||||
const [actRes, schRes, salesRes, budRes] = await Promise.all([
|
const [actRes, schRes, salesRes, budRes, pyActRes] = await Promise.all([
|
||||||
getActuals(fromStr, toStr),
|
getActuals(fromStr, toStr),
|
||||||
getScheduled(schedFrom, toStr),
|
getScheduled(schedFrom, toStr),
|
||||||
getNetSales(fromStr, salesTo), // salesTo caps at today for current month
|
getNetSales(fromStr, salesTo),
|
||||||
getBudgets(),
|
getBudgets(),
|
||||||
|
getActuals(pyFromStr, pyToStr),
|
||||||
])
|
])
|
||||||
setDepts(actRes.departments)
|
setDepts(actRes.departments)
|
||||||
setShowOncosts(actRes.show_oncosts)
|
setShowOncosts(actRes.show_oncosts)
|
||||||
|
|
@ -66,6 +73,12 @@ export default function Monthly() {
|
||||||
setScheduled(schMap)
|
setScheduled(schMap)
|
||||||
|
|
||||||
setNetSales(salesRes.days.reduce((s, d) => s + d.net_sales, 0))
|
setNetSales(salesRes.days.reduce((s, d) => s + d.net_sales, 0))
|
||||||
|
setPySales(salesRes.days.reduce((s, d) => s + d.py_sales, 0))
|
||||||
|
|
||||||
|
const pyTotal = pyActRes.departments.reduce(
|
||||||
|
(s, dep) => s + Object.values(dep.days).reduce((ds, d) => ds + d.cost, 0), 0
|
||||||
|
)
|
||||||
|
setPyWages(pyTotal > 0 ? pyTotal : null)
|
||||||
|
|
||||||
const bRow = budRes.budgets.find((b: WageBudget) => b.month === fromStr)
|
const bRow = budRes.budgets.find((b: WageBudget) => b.month === fromStr)
|
||||||
setBudget(bRow ? bRow.budget_amount : null)
|
setBudget(bRow ? bRow.budget_amount : null)
|
||||||
|
|
@ -74,7 +87,7 @@ export default function Monthly() {
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
}, [fromStr, toStr, schedFrom, salesTo])
|
}, [fromStr, toStr, schedFrom, salesTo, pyFromStr, pyToStr])
|
||||||
|
|
||||||
useEffect(() => { load() }, [load])
|
useEffect(() => { load() }, [load])
|
||||||
|
|
||||||
|
|
@ -118,6 +131,7 @@ export default function Monthly() {
|
||||||
const pctBudget = budget != null && budget > 0 ? (totalForecast / budget) * 100 : null
|
const pctBudget = budget != null && budget > 0 ? (totalForecast / budget) * 100 : null
|
||||||
const pctSales = netSales > 0 ? (totalActual / netSales) * 100 : null
|
const pctSales = netSales > 0 ? (totalActual / netSales) * 100 : null
|
||||||
const variance = budget != null ? totalForecast - budget : null
|
const variance = budget != null ? totalForecast - budget : null
|
||||||
|
const pyWagesDiff = pyWages != null && totalActual > 0 ? totalActual - pyWages : null
|
||||||
|
|
||||||
// Build chart data with per-dept costs per week so Bar dataKey works
|
// Build chart data with per-dept costs per week so Bar dataKey works
|
||||||
type WeekEntry = { label: string; isPast: boolean; [dept: string]: number | boolean | string }
|
type WeekEntry = { label: string; isPast: boolean; [dept: string]: number | boolean | string }
|
||||||
|
|
@ -174,6 +188,15 @@ export default function Monthly() {
|
||||||
<div className="label">{isCurrentMonth ? 'Actual MTD' : 'Actual'}</div>
|
<div className="label">{isCurrentMonth ? 'Actual MTD' : 'Actual'}</div>
|
||||||
<div className="value">{fmtMoney(totalActual)}</div>
|
<div className="value">{fmtMoney(totalActual)}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="summary-card">
|
||||||
|
<div className="label">{isCurrentMonth ? 'PY Wages MTD' : 'PY Wages'}</div>
|
||||||
|
<div className="value">{pyWages != null ? fmtMoney(pyWages) : '—'}</div>
|
||||||
|
{pyWagesDiff != null && (
|
||||||
|
<div className={`sub ${pyWagesDiff > 0 ? 'variance-over' : 'variance-under'}`}>
|
||||||
|
{pyWagesDiff > 0 ? '+' : ''}{fmtMoney(pyWagesDiff)} vs PY
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
{isCurrentMonth && (
|
{isCurrentMonth && (
|
||||||
<div className="summary-card">
|
<div className="summary-card">
|
||||||
<div className="label">Forecast EOM</div>
|
<div className="label">Forecast EOM</div>
|
||||||
|
|
@ -202,6 +225,10 @@ export default function Monthly() {
|
||||||
<div className="label">{isCurrentMonth ? 'Net Sales MTD' : 'Net Sales'}</div>
|
<div className="label">{isCurrentMonth ? 'Net Sales MTD' : 'Net Sales'}</div>
|
||||||
<div className="value">{fmtMoney(netSales)}</div>
|
<div className="value">{fmtMoney(netSales)}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="summary-card">
|
||||||
|
<div className="label">{isCurrentMonth ? 'PY Net Sales MTD' : 'PY Net Sales'}</div>
|
||||||
|
<div className="value">{pySales > 0 ? fmtMoney(pySales) : '—'}</div>
|
||||||
|
</div>
|
||||||
<div className="summary-card">
|
<div className="summary-card">
|
||||||
<div className="label">% Net Sales</div>
|
<div className="label">% Net Sales</div>
|
||||||
<div className="value">{pctSales != null ? `${pctSales.toFixed(1)}%` : '—'}</div>
|
<div className="value">{pctSales != null ? `${pctSales.toFixed(1)}%` : '—'}</div>
|
||||||
|
|
@ -239,6 +266,7 @@ export default function Monthly() {
|
||||||
<th>Department</th>
|
<th>Department</th>
|
||||||
<th className="right">{isCurrentMonth ? 'Actual MTD' : 'Actual'}</th>
|
<th className="right">{isCurrentMonth ? 'Actual MTD' : 'Actual'}</th>
|
||||||
{isCurrentMonth && <th className="right">Forecast → EOM</th>}
|
{isCurrentMonth && <th className="right">Forecast → EOM</th>}
|
||||||
|
<th className="right">% of Total</th>
|
||||||
<th className="right">Budget</th>
|
<th className="right">Budget</th>
|
||||||
<th className="right">% Budget</th>
|
<th className="right">% Budget</th>
|
||||||
<th className="right">Variance</th>
|
<th className="right">Variance</th>
|
||||||
|
|
@ -246,17 +274,22 @@ export default function Monthly() {
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{deptSummary.map(dep => {
|
{deptSummary.map(dep => {
|
||||||
const displayCost = isCurrentMonth ? dep.forecast_eom : dep.actual_mtd
|
const displayCost = isCurrentMonth ? dep.forecast_eom : dep.actual_mtd
|
||||||
|
const totalDisplay = isCurrentMonth ? totalForecast : totalActual
|
||||||
const depBudg = budget != null && (deptPcts[dep.department_id] ?? 0) > 0
|
const depBudg = budget != null && (deptPcts[dep.department_id] ?? 0) > 0
|
||||||
? budget * (deptPcts[dep.department_id] / 100)
|
? budget * (deptPcts[dep.department_id] / 100)
|
||||||
: null
|
: null
|
||||||
const dp = depBudg != null && depBudg > 0 ? (displayCost / depBudg) * 100 : null
|
const dp = depBudg != null && depBudg > 0 ? (displayCost / depBudg) * 100 : null
|
||||||
const dv = depBudg != null ? displayCost - depBudg : null
|
const dv = depBudg != null ? displayCost - depBudg : null
|
||||||
|
const depOfTotal = totalDisplay > 0 ? (displayCost / totalDisplay) * 100 : null
|
||||||
return (
|
return (
|
||||||
<tr key={dep.department_id}>
|
<tr key={dep.department_id}>
|
||||||
<td><span style={{ display: 'inline-block', width: 10, height: 10, borderRadius: '50%', background: dep.color, marginRight: 8 }} />{dep.department_name}</td>
|
<td><span style={{ display: 'inline-block', width: 10, height: 10, borderRadius: '50%', background: dep.color, marginRight: 8 }} />{dep.department_name}</td>
|
||||||
<td className="right">{fmtMoney(dep.actual_mtd)}</td>
|
<td className="right">{fmtMoney(dep.actual_mtd)}</td>
|
||||||
{isCurrentMonth && <td className="right">{fmtMoney(dep.forecast_eom)}</td>}
|
{isCurrentMonth && <td className="right">{fmtMoney(dep.forecast_eom)}</td>}
|
||||||
|
<td className="right" style={{ color: 'var(--text-muted)' }}>
|
||||||
|
{depOfTotal != null ? `${depOfTotal.toFixed(1)}%` : '—'}
|
||||||
|
</td>
|
||||||
<td className="right">{depBudg != null ? fmtMoney(depBudg) : '—'}</td>
|
<td className="right">{depBudg != null ? fmtMoney(depBudg) : '—'}</td>
|
||||||
<td className="right">
|
<td className="right">
|
||||||
{dp != null ? <span className={`pct-badge ${pctClass(dp)}`}>{dp.toFixed(1)}%</span> : '—'}
|
{dp != null ? <span className={`pct-badge ${pctClass(dp)}`}>{dp.toFixed(1)}%</span> : '—'}
|
||||||
|
|
@ -271,6 +304,7 @@ export default function Monthly() {
|
||||||
<td>Total</td>
|
<td>Total</td>
|
||||||
<td className="right">{fmtMoney(totalActual)}</td>
|
<td className="right">{fmtMoney(totalActual)}</td>
|
||||||
{isCurrentMonth && <td className="right">{fmtMoney(totalForecast)}</td>}
|
{isCurrentMonth && <td className="right">{fmtMoney(totalForecast)}</td>}
|
||||||
|
<td className="right">100%</td>
|
||||||
<td className="right">{budget != null ? fmtMoney(budget) : '—'}</td>
|
<td className="right">{budget != null ? fmtMoney(budget) : '—'}</td>
|
||||||
<td className="right">
|
<td className="right">
|
||||||
{pctBudget != null ? <span className={`pct-badge ${pctClass(pctBudget)}`}>{pctBudget.toFixed(1)}%</span> : '—'}
|
{pctBudget != null ? <span className={`pct-badge ${pctClass(pctBudget)}`}>{pctBudget.toFixed(1)}%</span> : '—'}
|
||||||
|
|
|
||||||
|
|
@ -46,10 +46,14 @@ export default function Weekly() {
|
||||||
|
|
||||||
const toStr = addDaysStr(fromStr, 6)
|
const toStr = addDaysStr(fromStr, 6)
|
||||||
const todayStr = localStr(new Date())
|
const todayStr = localStr(new Date())
|
||||||
|
// Prior-year equivalent week (52 weeks back)
|
||||||
|
const pyFromStr = addDaysStr(fromStr, -364)
|
||||||
|
const pyToStr = addDaysStr(toStr, -364)
|
||||||
|
|
||||||
const [depts, setDepts] = useState<DeptActuals[]>([])
|
const [depts, setDepts] = useState<DeptActuals[]>([])
|
||||||
const [netSales, setNetSales] = useState(0)
|
const [netSales, setNetSales] = useState(0)
|
||||||
const [pySales, setPySales] = useState(0)
|
const [pySales, setPySales] = useState(0)
|
||||||
|
const [pyWages, setPyWages] = useState<number | null>(null)
|
||||||
const [budget, setBudget] = useState<number | null>(null)
|
const [budget, setBudget] = useState<number | null>(null)
|
||||||
const [monthlyBudg, setMonthlyBudg] = useState<number | null>(null)
|
const [monthlyBudg, setMonthlyBudg] = useState<number | null>(null)
|
||||||
const [deptPcts, setDeptPcts] = useState<Record<string, number>>({})
|
const [deptPcts, setDeptPcts] = useState<Record<string, number>>({})
|
||||||
|
|
@ -60,10 +64,11 @@ export default function Weekly() {
|
||||||
const load = useCallback(async () => {
|
const load = useCallback(async () => {
|
||||||
setLoading(true); setError(null)
|
setLoading(true); setError(null)
|
||||||
try {
|
try {
|
||||||
const [actRes, salesRes, budgetRes] = await Promise.all([
|
const [actRes, salesRes, budgetRes, pyActRes] = await Promise.all([
|
||||||
getActuals(fromStr, toStr),
|
getActuals(fromStr, toStr),
|
||||||
getNetSales(fromStr, toStr),
|
getNetSales(fromStr, toStr),
|
||||||
getBudgets(),
|
getBudgets(),
|
||||||
|
getActuals(pyFromStr, pyToStr),
|
||||||
])
|
])
|
||||||
setDepts(actRes.departments)
|
setDepts(actRes.departments)
|
||||||
setShowOncosts(actRes.show_oncosts)
|
setShowOncosts(actRes.show_oncosts)
|
||||||
|
|
@ -71,6 +76,11 @@ export default function Weekly() {
|
||||||
setNetSales(salesRes.days.reduce((s, d) => s + d.net_sales, 0))
|
setNetSales(salesRes.days.reduce((s, d) => s + d.net_sales, 0))
|
||||||
setPySales(salesRes.days.reduce((s, d) => s + d.py_sales, 0))
|
setPySales(salesRes.days.reduce((s, d) => s + d.py_sales, 0))
|
||||||
|
|
||||||
|
const pyTotal = pyActRes.departments.reduce(
|
||||||
|
(s, dep) => s + Object.values(dep.days).reduce((ds, d) => ds + d.cost, 0), 0
|
||||||
|
)
|
||||||
|
setPyWages(pyTotal > 0 ? pyTotal : null)
|
||||||
|
|
||||||
const d0 = new Date(fromStr + 'T00:00:00')
|
const d0 = new Date(fromStr + 'T00:00:00')
|
||||||
const monthKey = `${d0.getFullYear()}-${String(d0.getMonth() + 1).padStart(2, '0')}-01`
|
const monthKey = `${d0.getFullYear()}-${String(d0.getMonth() + 1).padStart(2, '0')}-01`
|
||||||
const bRow = (budgetRes.budgets as WageBudget[]).find(b => b.month === monthKey)
|
const bRow = (budgetRes.budgets as WageBudget[]).find(b => b.month === monthKey)
|
||||||
|
|
@ -93,7 +103,7 @@ export default function Weekly() {
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
}, [fromStr, toStr, todayStr]) // all primitive strings — stable refs
|
}, [fromStr, toStr, todayStr, pyFromStr, pyToStr])
|
||||||
|
|
||||||
useEffect(() => { load() }, [load])
|
useEffect(() => { load() }, [load])
|
||||||
|
|
||||||
|
|
@ -101,7 +111,6 @@ export default function Weekly() {
|
||||||
const next = () => setFromStr(s => addDaysStr(s, 7))
|
const next = () => setFromStr(s => addDaysStr(s, 7))
|
||||||
const isCurrentWeek = fromStr === mondayOf(new Date())
|
const isCurrentWeek = fromStr === mondayOf(new Date())
|
||||||
|
|
||||||
// dept_pct × (weekly pro-rata) = dept weekly budget
|
|
||||||
const deptWeekBudget = (deptId: string) =>
|
const deptWeekBudget = (deptId: string) =>
|
||||||
budget != null && monthlyBudg != null && (deptPcts[deptId] ?? 0) > 0
|
budget != null && monthlyBudg != null && (deptPcts[deptId] ?? 0) > 0
|
||||||
? budget * (deptPcts[deptId] / 100)
|
? budget * (deptPcts[deptId] / 100)
|
||||||
|
|
@ -116,6 +125,7 @@ export default function Weekly() {
|
||||||
const totalWages = deptTotals.reduce((s, d) => s + d.cost, 0)
|
const totalWages = deptTotals.reduce((s, d) => s + d.cost, 0)
|
||||||
const pctBudget = budget != null && budget > 0 ? (totalWages / budget) * 100 : null
|
const pctBudget = budget != null && budget > 0 ? (totalWages / budget) * 100 : null
|
||||||
const pctSales = netSales > 0 ? (totalWages / netSales) * 100 : null
|
const pctSales = netSales > 0 ? (totalWages / netSales) * 100 : null
|
||||||
|
const pyWagesDiff = pyWages != null && totalWages > 0 ? totalWages - pyWages : null
|
||||||
|
|
||||||
const weekLabel = `${fmtDisplay(fromStr)} – ${fmtDisplay(toStr)} ${new Date(toStr + 'T00:00:00').getFullYear()}`
|
const weekLabel = `${fmtDisplay(fromStr)} – ${fmtDisplay(toStr)} ${new Date(toStr + 'T00:00:00').getFullYear()}`
|
||||||
|
|
||||||
|
|
@ -140,6 +150,15 @@ export default function Weekly() {
|
||||||
<div className="value">{fmtMoney(totalWages)}</div>
|
<div className="value">{fmtMoney(totalWages)}</div>
|
||||||
<div className="sub">{showOncosts ? 'incl. on-costs' : 'base cost'}</div>
|
<div className="sub">{showOncosts ? 'incl. on-costs' : 'base cost'}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="summary-card">
|
||||||
|
<div className="label">PY Wages</div>
|
||||||
|
<div className="value">{pyWages != null ? fmtMoney(pyWages) : '—'}</div>
|
||||||
|
{pyWagesDiff != null && (
|
||||||
|
<div className={`sub ${pyWagesDiff > 0 ? 'variance-over' : 'variance-under'}`}>
|
||||||
|
{pyWagesDiff > 0 ? '+' : ''}{fmtMoney(pyWagesDiff)} vs PY
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<div className="summary-card">
|
<div className="summary-card">
|
||||||
<div className="label">Pro-rata Budget</div>
|
<div className="label">Pro-rata Budget</div>
|
||||||
<div className="value">{budget != null ? fmtMoney(budget) : '—'}</div>
|
<div className="value">{budget != null ? fmtMoney(budget) : '—'}</div>
|
||||||
|
|
@ -156,7 +175,10 @@ export default function Weekly() {
|
||||||
<div className="summary-card">
|
<div className="summary-card">
|
||||||
<div className="label">Net Sales</div>
|
<div className="label">Net Sales</div>
|
||||||
<div className="value">{fmtMoney(netSales)}</div>
|
<div className="value">{fmtMoney(netSales)}</div>
|
||||||
{pySales > 0 && <div className="sub">PY {fmtMoney(pySales)}</div>}
|
</div>
|
||||||
|
<div className="summary-card">
|
||||||
|
<div className="label">PY Net Sales</div>
|
||||||
|
<div className="value">{pySales > 0 ? fmtMoney(pySales) : '—'}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="summary-card">
|
<div className="summary-card">
|
||||||
<div className="label">% of Net Sales</div>
|
<div className="label">% of Net Sales</div>
|
||||||
|
|
@ -174,28 +196,31 @@ export default function Weekly() {
|
||||||
<tr>
|
<tr>
|
||||||
<th>Department</th>
|
<th>Department</th>
|
||||||
<th className="right">Wages</th>
|
<th className="right">Wages</th>
|
||||||
|
<th className="right">% of Total</th>
|
||||||
<th className="right">Budget (pro-rata)</th>
|
<th className="right">Budget (pro-rata)</th>
|
||||||
<th className="right">% Budget</th>
|
<th className="right">% Budget</th>
|
||||||
<th className="right">Net Sales</th>
|
|
||||||
<th className="right">% Net Sales</th>
|
<th className="right">% Net Sales</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{deptTotals.map(dep => {
|
{deptTotals.map(dep => {
|
||||||
const depBudg = deptWeekBudget(dep.department_id)
|
const depBudg = deptWeekBudget(dep.department_id)
|
||||||
const depPct = depBudg != null && depBudg > 0 ? (dep.cost / depBudg) * 100 : null
|
const depPct = depBudg != null && depBudg > 0 ? (dep.cost / depBudg) * 100 : null
|
||||||
const depSPct = netSales > 0 ? (dep.cost / netSales) * 100 : null
|
const depOfTotal = totalWages > 0 ? (dep.cost / totalWages) * 100 : null
|
||||||
|
const depSPct = netSales > 0 ? (dep.cost / netSales) * 100 : null
|
||||||
return (
|
return (
|
||||||
<tr key={dep.department_name}>
|
<tr key={dep.department_name}>
|
||||||
<td>{dep.department_name}</td>
|
<td>{dep.department_name}</td>
|
||||||
<td className="right">{fmtMoney(dep.cost)}</td>
|
<td className="right">{fmtMoney(dep.cost)}</td>
|
||||||
|
<td className="right" style={{ color: 'var(--text-muted)' }}>
|
||||||
|
{depOfTotal != null ? `${depOfTotal.toFixed(1)}%` : '—'}
|
||||||
|
</td>
|
||||||
<td className="right">{depBudg != null ? fmtMoney(depBudg) : '—'}</td>
|
<td className="right">{depBudg != null ? fmtMoney(depBudg) : '—'}</td>
|
||||||
<td className="right">
|
<td className="right">
|
||||||
{depPct != null
|
{depPct != null
|
||||||
? <span className={`pct-badge ${pctClass(depPct)}`}>{depPct.toFixed(1)}%</span>
|
? <span className={`pct-badge ${pctClass(depPct)}`}>{depPct.toFixed(1)}%</span>
|
||||||
: '—'}
|
: '—'}
|
||||||
</td>
|
</td>
|
||||||
<td className="right">{fmtMoney(netSales)}</td>
|
|
||||||
<td className="right">{depSPct != null ? `${depSPct.toFixed(1)}%` : '—'}</td>
|
<td className="right">{depSPct != null ? `${depSPct.toFixed(1)}%` : '—'}</td>
|
||||||
</tr>
|
</tr>
|
||||||
)
|
)
|
||||||
|
|
@ -203,13 +228,13 @@ export default function Weekly() {
|
||||||
<tr className="total-row">
|
<tr className="total-row">
|
||||||
<td>Total</td>
|
<td>Total</td>
|
||||||
<td className="right">{fmtMoney(totalWages)}</td>
|
<td className="right">{fmtMoney(totalWages)}</td>
|
||||||
|
<td className="right">100%</td>
|
||||||
<td className="right">{budget != null ? fmtMoney(budget) : '—'}</td>
|
<td className="right">{budget != null ? fmtMoney(budget) : '—'}</td>
|
||||||
<td className="right">
|
<td className="right">
|
||||||
{pctBudget != null
|
{pctBudget != null
|
||||||
? <span className={`pct-badge ${pctClass(pctBudget)}`}>{pctBudget.toFixed(1)}%</span>
|
? <span className={`pct-badge ${pctClass(pctBudget)}`}>{pctBudget.toFixed(1)}%</span>
|
||||||
: '—'}
|
: '—'}
|
||||||
</td>
|
</td>
|
||||||
<td className="right">{fmtMoney(netSales)}</td>
|
|
||||||
<td className="right">{pctSales != null ? `${pctSales.toFixed(1)}%` : '—'}</td>
|
<td className="right">{pctSales != null ? `${pctSales.toFixed(1)}%` : '—'}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue