From 7ed585d6a9de3e406f46ecea4f4a3ae67069a9cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=97=D0=B0=D0=B8=D0=B4=20=D0=9E=D0=BC=D0=B0=D1=80=20?= =?UTF-8?q?=D0=9C=D0=B5=D0=B4=D1=85=D0=B0=D1=82?= Date: Sun, 23 Aug 2026 05:33:52 +0500 Subject: [PATCH] Show ingredient tier/enchant + price age in haul advisor - Opportunities breakdown now shows each ingredient's tier/enchant (e.g. T6.4) so enchant mismatches are visible at a glance. - Travel haul advisor now shows color-coded price-age badges (green fresh / yellow aging / red stale) for both buy and sell prices, like Opportunities already does. Note: the enchanted-ingredient pricing itself was already correct in code (T6_SHOES_PLATE_SET1@4); a deployed DB parsed before that fix must re-run Game Data -> Re-download to pick it up. Co-Authored-By: Claude Fable 5 --- server/src/travel/route.ts | 4 ++++ web/src/pages/OpportunitiesPage.tsx | 15 ++++++++++++++- web/src/pages/TravelPage.tsx | 14 +++++++++++++- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/server/src/travel/route.ts b/server/src/travel/route.ts index 2fbe961..39368d0 100644 --- a/server/src/travel/route.ts +++ b/server/src/travel/route.ts @@ -141,6 +141,8 @@ export interface HaulItem { weight: number; buyAt: number; // sell-order price in origin (you buy instantly) sellAt: number; // sell-order price in destination + buyAgeHours: number | null; // how stale the origin price is + sellAgeHours: number | null; // how stale the destination price is netPerUnit: number; // after taxes at destination unitsCarryable: number; tripProfit: number; @@ -177,6 +179,8 @@ export async function haulAdvisor(from: string, to: string, maxWeight: number, m weight: it.weight, buyAt: buy.price, sellAt: sell.price, + buyAgeHours: buy.ageHours, + sellAgeHours: sell.ageHours, netPerUnit, unitsCarryable, tripProfit: netPerUnit * unitsCarryable, diff --git a/web/src/pages/OpportunitiesPage.tsx b/web/src/pages/OpportunitiesPage.tsx index 0ba72f9..77adc35 100644 --- a/web/src/pages/OpportunitiesPage.tsx +++ b/web/src/pages/OpportunitiesPage.tsx @@ -41,6 +41,14 @@ const CITIES = ['Bridgewatch', 'Caerleon', 'Fort Sterling', 'Lymhurst', 'Martloc const silver = (n: number) => Math.round(n).toLocaleString('en-US'); +/** Derive a "T6.4" chip from an item id like "T6_SHOES_PLATE_SET1@4" (blank for non-tiered ids). */ +function tierEnchant(id: string): string { + const t = id.match(/^T(\d)_/); + if (!t) return ''; + const e = id.match(/@(\d)$/); + return `T${t[1]}${e ? `.${e[1]}` : ''}`; +} + function Age({ hours }: { hours: number | null }) { if (hours == null) return null; const label = hours < 1 ? `${Math.round(hours * 60)}m` : hours < 48 ? `${Math.round(hours)}h` : `${Math.round(hours / 24)}d`; @@ -218,7 +226,12 @@ export default function OpportunitiesPage() { {o.inputs.map((inp) => ( - {o.inputNames[inp.item] ?? inp.item} + + {o.inputNames[inp.item] ?? inp.item} + {tierEnchant(inp.item) && ( + {tierEnchant(inp.item)} + )} + {inp.count} {inp.effectiveCount.toFixed(2)} {silver(inp.unitCost)} diff --git a/web/src/pages/TravelPage.tsx b/web/src/pages/TravelPage.tsx index 84f60ef..41dbb5b 100644 --- a/web/src/pages/TravelPage.tsx +++ b/web/src/pages/TravelPage.tsx @@ -26,12 +26,22 @@ interface HaulItem { weight: number; buyAt: number; sellAt: number; + buyAgeHours: number | null; + sellAgeHours: number | null; netPerUnit: number; unitsCarryable: number; tripProfit: number; profitPerWeight: number; } +/** Color-coded price-age badge: green fresh, yellow aging, red stale. */ +function PriceAge({ hours }: { hours: number | null }) { + if (hours == null) return —; + const label = hours < 1 ? `${Math.round(hours * 60)}m` : hours < 48 ? `${Math.round(hours)}h` : `${Math.round(hours / 24)}d`; + const color = hours < 6 ? '#7fd18a' : hours < 24 ? '#e0c069' : '#e08869'; + return {label}; +} + interface Status { zones: number; connections: number; @@ -218,7 +228,7 @@ export default function TravelPage() { - + @@ -226,7 +236,9 @@ export default function TravelPage() { + +
ItemBuy at {from}Sell at {to}Net/unitUnitsTrip profitProfit/kgItemBuy at {from}ageSell at {to}ageNet/unitUnitsTrip profitProfit/kg
{h.name ?? h.item} T{h.tier} {silver(h.buyAt)} {silver(h.sellAt)} {silver(h.netPerUnit)} {h.unitsCarryable} {silver(h.tripProfit)}