mega-plant/components/VehicleSiteHistory.tsx
Dave 1429c8087b feat(plant): vehicle site history from Fuel Logs
/plant and /plant/map import Fuel Logs CSVs by exact QR and show each
vehicle's current and previous distinct site with the dates they were
observed.

- plant_site_observations is durable, QR-keyed history. It is never wiped
  and a previous site is never manufactured.
- An older or repeated snapshot must not move a vehicle backwards; untimed
  conflicting sites on one day are held for review.
- A manual site change shares the same history and the same lock.
- Current/previous metadata and the sticker QR are captured BEFORE the asset
  rows are deleted in the seed, so a re-seed cannot wipe them. Explicit
  empty values are preserved too: clearing a site is a deliberate manual
  observation, not permission to restore an old sheet value.
- Imports change location metadata only. They never touch income, cost or
  fuel measurements.
- A new named site is created only for a matched Plant vehicle. Missing
  coordinates mean "awaiting a pin", never a headquarters fallback.

Owner-confirmed 2026-11-04: TLB06's QR is 1089, not the old meter reading
4704. lib/plant-site-policy.json holds the confirmed aliases (Equestria
Bastion Job -> Equestria 215, Cosmo City / Lion Park -> Cosmo City); no
other identity is guessed.
2026-11-05 00:18:24 +02:00

28 lines
1.6 KiB
TypeScript

type SiteHistory = {
site_label?: string;
previous_site_label?: string;
site_observed_on?: string;
previous_site_observed_on?: string;
site_source?: string;
};
export function siteSeenDate(value?: string) {
if (!value) return "Unknown";
const date = new Date(value.length === 10 ? `${value}T12:00:00Z` : value);
return Number.isNaN(date.getTime()) ? "Unknown" : date.toLocaleDateString("en-ZA", { day: "numeric", month: "short", year: "numeric", timeZone: "Africa/Johannesburg" });
}
export function VehicleSiteHistory({ asset, currentSite, previousSite }: {
asset: SiteHistory;
currentSite?: string | null;
previousSite?: string;
}) {
const source = asset.site_source === "fuel-log" ? "Fuel log" : asset.site_source === "manual" ? "Manual assignment" : "Source not recorded";
return (
<dl className="space-y-1.5 text-xs leading-relaxed text-gray-600 dark:text-gray-300">
<div><dt className="font-medium text-[#003D5B] dark:text-white">Current site</dt><dd className="break-words">{asset.site_label || currentSite || "Unassigned"}</dd></div>
<div><dt className="font-medium text-[#003D5B] dark:text-white">Previous site</dt><dd className="break-words">{asset.previous_site_label || previousSite || "Unknown — no earlier different site recorded"}{asset.previous_site_observed_on && <span className="block text-gray-500 dark:text-gray-400">{siteSeenDate(asset.previous_site_observed_on)}</span>}</dd></div>
<div><dt className="font-medium text-[#003D5B] dark:text-white">Last seen</dt><dd>{siteSeenDate(asset.site_observed_on)} · {source}</dd></div>
</dl>
);
}