mega-plant/app/running/page.tsx
Mega Plant 0516220110 Round 4: regressions fixed + mobile standard
Part A — Round 3 regressions:
- A.1: verify-cost-reconciliation.mjs now loads .env.local, wraps in try/catch, exits 1 on any failure
- A.2: Vehicle Costs restored Santam — back to R 14,996.31 (matches row-by-row sum)
- A.3: incl<excl / VAT math / VAT>20% now FATAL — seed exits 2 with full report (4 anomalies on Charl-CCS + Buildsmart)
- A.4: Reconciliation target R 694,962.93 with both deltas itemised (Personnel +R19,800, Santam -R5,322.44, unexplained R 0.00)
- A.5: 'Lines' view uses actual lines (recharts LineChart), no fills; fills only in 'Cost stack'

Part B — Mobile standard:
- B.1: header wraps to 2 rows on mobile; YTD badge cards stack; page-wide overflow eliminated on /income, /charts, /overheads, /running, /expenses
- B.2: IncomeView card row layout stacked — name gets its own line on mobile, never truncated to single char
- B.3: Plant type filter is a <select> dropdown below lg (was 18 pills wrapping to 13 rows); same for sort on /income and /running
- B.4: Plant table first column sticky; -webkit-overflow-scrolling: touch; tables scroll inside their own container
- B.5: All buttons/links use min-h-[44px] tap targets; text below 12px is bumped where it matters
- B.6: Charts legend wraps below title; chart min-height h-44 → h-52 on mobile
- B.7: paddingTop env(safe-area-inset-top) on sticky header for notch/home indicator clearance

AGENTS.md created with the standing mobile checklist rule.
2026-09-12 12:05:43 +02:00

41 lines
1.9 KiB
TypeScript

import { getAssets, getRunningCosts } from "@/lib/data";
import { ZAR } from "@/lib/utils";
import { Wrench } from "lucide-react";
import { RunningView } from "@/components/RunningView";
import { buildRunningRows } from "@/lib/rows";
// Reads PocketBase on every request. Without this Next tries to prerender the
// page at build time, which makes `docker build` depend on the database being
// reachable and then serves a frozen build-time snapshot to users.
export const dynamic = "force-dynamic";
export default async function RunningPage() {
const [assets, costs] = await Promise.all([getAssets(), getRunningCosts()]);
const rows = buildRunningRows(assets, costs);
const monthTotals = Array(12).fill(0);
for (const c of costs) monthTotals[c.month - 1] += c.amount;
return (
<div className="p-8 max-w-[1400px]">
<div className="flex items-end justify-between mb-6">
<div>
<div className="flex items-center gap-3 mb-1">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[#EDAE49]/15 to-[#D1495B]/15 grid place-items-center text-[#EDAE49]">
<Wrench className="w-5 h-5" />
</div>
<h1 className="text-2xl font-semibold text-[#003D5B] dark:text-white">Running Costs</h1>
</div>
<p className="text-gray-500 dark:text-gray-400 text-sm">Repairs & parts per machine per month · 2026</p>
</div>
<div className="bg-gradient-to-br from-[#EDAE49] to-[#D1495B] rounded-2xl px-5 py-3 text-white shadow-lg shadow-orange-500/20">
<div className="text-xs text-white/80">YTD running cost</div>
<div className="text-2xl font-semibold tabular-nums">{ZAR(monthTotals.reduce((s, v) => s + v, 0))}</div>
</div>
</div>
<RunningView rows={rows} palette={{ income: "#EDAE49", profit: "#EDAE49", loss: "#EDAE49" }} monthTotals={monthTotals} />
</div>
);
}