From a60303cb1a93b9853f8acedc26c60acc5b39a28b Mon Sep 17 00:00:00 2001 From: Jan Date: Tue, 13 Oct 2026 23:08:46 +0200 Subject: [PATCH 1/2] feat(fio): fio burn runway strips per plan and runway bars per material, amber yellow threshold MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Plan Burn Overview: a strip per plan (PStripPlot) with every consumed material's days left on a 0..3x-yellow axis, red and amber zones, "+n" beyond the axis and a "1 red · 2 yellow · 10 materials" summary line - strip dots never overlap (lanes plus sideways dodge); hover/focus shows the dot, a touch tap lists every material under the finger - expanded plan: "runs out first" line and a Runway column (PRunwayBar) with dashed thresholds; consumption and production hide below 1600 px - yellow burn badges are amber (bg-warning) everywhere, including XIT - PDataTable: expanded and empty rows are no longer 20 px wider than their box; FIOBurnView grid no longer grows without limit after an expand Spec: specs/2026-10-13-charts-09-fio-burn-runway.md Co-Authored-By: Claude Opus 5.5 --- .../fio/components/FIOBurnPlanTable.vue | 92 +++++++- src/features/fio/components/FIOBurnTable.vue | 198 ++++++++++++---- src/features/fio/fioBurn.util.ts | 48 ++++ src/features/fio/useFIOBurn.ts | 1 + src/features/fio/useFIOBurn.types.ts | 1 + src/features/preferences/usePreferences.ts | 2 +- src/locales/en_US/common.json | 7 + src/locales/en_US/fio.json | 21 +- .../fio/components/FIOBurnPlanTable.test.ts | 139 ++++++++++-- .../fio/components/FIOBurnTable.test.ts | 99 +++++++- src/tests/features/fio/fioBurn.util.test.ts | 51 +++++ src/tests/features/fio/useFIOBurn.test.ts | 1 + .../preferences/usePreferences.test.ts | 4 +- .../components/XITBurnActionButton.test.ts | 2 +- src/tests/ui/charts/PRunwayBar.test.ts | 58 +++++ src/tests/ui/charts/PStripPlot.test.ts | 152 +++++++++++++ src/tests/ui/charts/charts.util.test.ts | 113 +++++++++- src/ui/charts/PRunwayBar.vue | 79 +++++++ src/ui/charts/PStripPlot.vue | 212 ++++++++++++++++++ src/ui/charts/charts.types.ts | 15 ++ src/ui/charts/charts.util.ts | 83 ++++++- src/ui/components/PDataTable.vue | 9 +- src/views/fio/FIOBurnView.vue | 2 +- 23 files changed, 1307 insertions(+), 82 deletions(-) create mode 100644 src/features/fio/fioBurn.util.ts create mode 100644 src/tests/features/fio/fioBurn.util.test.ts create mode 100644 src/tests/ui/charts/PRunwayBar.test.ts create mode 100644 src/tests/ui/charts/PStripPlot.test.ts create mode 100644 src/ui/charts/PRunwayBar.vue create mode 100644 src/ui/charts/PStripPlot.vue diff --git a/src/features/fio/components/FIOBurnPlanTable.vue b/src/features/fio/components/FIOBurnPlanTable.vue index c1c7b541b..22100e5cd 100644 --- a/src/features/fio/components/FIOBurnPlanTable.vue +++ b/src/features/fio/components/FIOBurnPlanTable.vue @@ -5,14 +5,17 @@ const { t } = useI18n(); // Composables - import { usePlanetData } from "@/database/services/usePlanetData"; import { usePreferences } from "@/features/preferences/usePreferences"; - const { getBurnDisplayClass } = usePreferences(); - const { planetName } = usePlanetData(); + const { getBurnDisplayClass, burnDaysRed, burnDaysYellow } = + usePreferences(); + + // Components + import MaterialTile from "@/features/material_tile/components/MaterialTile.vue"; // Util import { formatNumber } from "@/util/numbers"; + import { burnAxisMax, burnSummary } from "@/features/fio/fioBurn.util"; // Types & Interfaces import type { IFIOBurnPlanetTableElement } from "@/features/fio/useFIOBurn.types"; @@ -20,6 +23,7 @@ // UI import { PDataTable } from "@/ui"; + import PStripPlot from "@/ui/charts/PStripPlot.vue"; defineProps({ planTable: { @@ -28,6 +32,8 @@ }, }); + const axis = computed(() => burnAxisMax(burnDaysYellow.value)); + const columns = computed[]>( () => [ { @@ -35,10 +41,12 @@ title: t("fio.burn.components.overview.table.plan"), // by the name shown, not the id sorter: (a, b) => a.planName.localeCompare(b.planName), + minWidth: 120, }, { - key: "planetId", - title: t("fio.burn.components.overview.table.planet"), + key: "strip", + title: t("fio.burn.components.overview.table.strip"), + minWidth: 180, }, { key: "minDays", @@ -47,28 +55,96 @@ }, ] ); + + // "1 red · 2 yellow · 10 materials", zero counts left out + function summary(row: IFIOBurnPlanetTableElement): string[] { + const s = burnSummary( + row.burnMaterials, + burnDaysRed.value, + burnDaysYellow.value + ); + const prefix = "fio.burn.components.overview.summary"; + return [ + s.red ? t(`${prefix}.red`, { n: s.red }) : "", + s.yellow ? t(`${prefix}.yellow`, { n: s.yellow }) : "", + t(`${prefix}.materials`, s.consumed), + ].filter(Boolean); + } + + const stripItems = (row: IFIOBurnPlanetTableElement) => + row.burnMaterials.map((m) => ({ key: m.ticker, value: m.exhaustion }));