From ff145957fcdf9f51f7b9734d6004fb0e9cc3b6cd Mon Sep 17 00:00:00 2001 From: jtricerolph Date: Thu, 30 Jul 2026 09:46:13 +0000 Subject: [PATCH] Trim flow chevrons to fit one row, add water-passing indicator Regen flow indicators now show 2 chevrons instead of 3 so the diagram row fits without wrapping on narrow cards. Added a pulsing vertical chevron stack under each tank's % reading, shown when that tank is actually in-service and passing water (not regenerating). --- .../src/components/WaterSoftenerDiagram.tsx | 17 ++++++++++++++--- frontend/src/index.css | 18 ++++++++++++++++-- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/WaterSoftenerDiagram.tsx b/frontend/src/components/WaterSoftenerDiagram.tsx index 491259d..b31c995 100644 --- a/frontend/src/components/WaterSoftenerDiagram.tsx +++ b/frontend/src/components/WaterSoftenerDiagram.tsx @@ -1,4 +1,4 @@ -import { RefreshCw, ChevronsRight, ChevronsLeft } from 'lucide-react' +import { RefreshCw, ChevronsRight, ChevronsLeft, ChevronsDown } from 'lucide-react' import type { AssetStatus, TelemetryField } from '../types' function fieldByKey(latest: TelemetryField[], key: string | null): TelemetryField | undefined { @@ -44,6 +44,10 @@ function Tank({ label, pct, valueLabel, variant, regenerating }: { regenerating: boolean }) { const clamped = pct === null ? 0 : Math.max(0, Math.min(100, pct)) + // Water is passing through whichever tank is actually in service right now + // (or the single tank, when it's not offline regenerating) — shown as + // pulsing down-chevrons under the % reading. + const passingWater = !regenerating && variant !== 'standby' return (
{variant === 'active' && ( @@ -54,7 +58,15 @@ function Tank({ label, pct, valueLabel, variant, regenerating }: { )}
-
{pct === null ? '—' : `${Math.round(pct)}%`}
+
+
{pct === null ? '—' : `${Math.round(pct)}%`}
+ {passingWater && ( + + )} +
{label}
{valueLabel}
@@ -105,7 +117,6 @@ export default function WaterSoftenerDiagram({ asset }: { asset: AssetStatus }) ) } diff --git a/frontend/src/index.css b/frontend/src/index.css index f84a2da..0149665 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -249,7 +249,6 @@ html, body, #root { height: 100%; margin: 0; font-size: 14px; } .softener-flow-left svg { margin-right: -6px; } .softener-flow-active svg { color: var(--app-primary); animation: softener-flow-pulse 1.1s ease-in-out infinite; } .softener-flow-active svg:nth-child(2) { animation-delay: .15s; } -.softener-flow-active svg:nth-child(3) { animation-delay: .3s; } @keyframes softener-flow-pulse { 0%, 100% { opacity: .25; } 50% { opacity: 1; } @@ -304,14 +303,29 @@ html, body, #root { height: 100%; margin: 0; font-size: 14px; } .softener-fill-warning { background: var(--sev-warning); } .softener-fill-critical { background: var(--sev-critical); } .softener-fill-unknown { background: var(--card-border); height: 0 !important; } -.softener-tank-pct { +.softener-tank-content { position: relative; z-index: 1; + display: flex; + flex-direction: column; + align-items: center; + gap: 2px; +} +.softener-tank-pct { font-size: 12px; font-weight: 700; color: var(--text-dark); text-shadow: 0 1px 2px rgba(255,255,255,.6); } +.softener-tank-water { + display: flex; + flex-direction: column; + align-items: center; + color: var(--app-primary); + filter: drop-shadow(0 1px 1px rgba(255,255,255,.6)); +} +.softener-tank-water svg { margin-top: -4px; animation: softener-flow-pulse 1.1s ease-in-out infinite; } +.softener-tank-water svg:nth-child(2) { animation-delay: .2s; } .softener-tank-label { font-size: 11px; font-weight: 600; color: var(--text-dark); margin-top: 6px; } .softener-tank-value { font-size: 10px; color: var(--text-mid); }