Show muted icons on intermediate rota footer rows

vs Booked, with Recurring Tasks, and with Pickup now show ⚠/✓/✗
in muted grey using the same amber thresholds as the final row,
giving a quick visual cue without the coloured emphasis.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jtricerolph 2026-07-22 13:41:07 +00:00
parent 75ac7591d2
commit 1a8d8d9a2b
5 changed files with 25 additions and 149 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -5,7 +5,7 @@
<meta name="viewport" content="width=1280" />
<meta name="theme-color" content="#2d6a4f" />
<title>HK Planner</title>
<script type="module" crossorigin src="/hk-planner/assets/index-DYQJ_2ny.js"></script>
<script type="module" crossorigin src="/hk-planner/assets/index-DOMhnWTP.js"></script>
<link rel="stylesheet" crossorigin href="/hk-planner/assets/index-B7_UXJgZ.css">
<link rel="manifest" href="/hk-planner/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/hk-planner/registerSW.js"></script></head>
<body>

2
frontend/dist/sw.js vendored
View file

@ -1 +1 @@
if(!self.define){let e,i={};const n=(n,s)=>(n=new URL(n+".js",s).href,i[n]||new Promise(i=>{if("document"in self){const e=document.createElement("script");e.src=n,e.onload=i,document.head.appendChild(e)}else e=n,importScripts(n),i()}).then(()=>{let e=i[n];if(!e)throw new Error(`Module ${n} didnt register its module`);return e}));self.define=(s,r)=>{const o=e||("document"in self?document.currentScript.src:"")||location.href;if(i[o])return;let t={};const c=e=>n(e,o),d={module:{uri:o},exports:t,require:c};i[o]=Promise.all(s.map(e=>d[e]||c(e))).then(e=>(r(...e),t))}}define(["./workbox-9c191d2f"],function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"registerSW.js",revision:"a2c395d8c225f1b3ea12388f15189bce"},{url:"index.html",revision:"0913278d5fa0fe282bff5174b4656bbf"},{url:"icons/icon-512.png",revision:"c32202b9deed67ef38331f63dec9d1c8"},{url:"icons/icon-192.png",revision:"46ece317d50d10b8f5e225e073b3221d"},{url:"assets/index-DYQJ_2ny.js",revision:null},{url:"assets/index-B7_UXJgZ.css",revision:null},{url:"manifest.webmanifest",revision:"c2510de876adb84db0c4b300b71216fa"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("/hk-planner/index.html"),{denylist:[/\/api\//]}))});
if(!self.define){let e,i={};const n=(n,s)=>(n=new URL(n+".js",s).href,i[n]||new Promise(i=>{if("document"in self){const e=document.createElement("script");e.src=n,e.onload=i,document.head.appendChild(e)}else e=n,importScripts(n),i()}).then(()=>{let e=i[n];if(!e)throw new Error(`Module ${n} didnt register its module`);return e}));self.define=(s,r)=>{const o=e||("document"in self?document.currentScript.src:"")||location.href;if(i[o])return;let t={};const d=e=>n(e,o),c={module:{uri:o},exports:t,require:d};i[o]=Promise.all(s.map(e=>c[e]||d(e))).then(e=>(r(...e),t))}}define(["./workbox-9c191d2f"],function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"registerSW.js",revision:"a2c395d8c225f1b3ea12388f15189bce"},{url:"index.html",revision:"dac594c6fb03224d7ef693607551dd8a"},{url:"icons/icon-512.png",revision:"c32202b9deed67ef38331f63dec9d1c8"},{url:"icons/icon-192.png",revision:"46ece317d50d10b8f5e225e073b3221d"},{url:"assets/index-DOMhnWTP.js",revision:null},{url:"assets/index-B7_UXJgZ.css",revision:null},{url:"manifest.webmanifest",revision:"c2510de876adb84db0c4b300b71216fa"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("/hk-planner/index.html"),{denylist:[/\/api\//]}))});

View file

@ -915,21 +915,21 @@ function StaffTable({ bookings, staff, required, warnOverRed, warnOverAmber, war
<tr>
<td className="col-label" style={{ fontSize: '0.72rem', color: 'var(--text-mid)' }}>vs Booked</td>
{dates.map(date => (
<td key={date}><PlainDiffCell available={avail[date]} required={required[date].booked} /></td>
<td key={date}><PlainDiffCell available={avail[date]} required={required[date].booked} warnOverAmber={warnOverAmber} warnUnderAmber={warnUnderAmber} /></td>
))}
<td />
</tr>
<tr>
<td className="col-label" style={{ fontSize: '0.72rem', color: 'var(--text-mid)' }}>with Recurring Tasks</td>
{dates.map(date => (
<td key={date}><PlainDiffCell available={avail[date]} required={required[date].booked + required[date].general} /></td>
<td key={date}><PlainDiffCell available={avail[date]} required={required[date].booked + required[date].general} warnOverAmber={warnOverAmber} warnUnderAmber={warnUnderAmber} /></td>
))}
<td />
</tr>
<tr>
<td className="col-label" style={{ fontSize: '0.72rem', color: 'var(--text-mid)' }}>with Pickup</td>
{dates.map(date => (
<td key={date}><PlainDiffCell available={avail[date]} required={required[date].booked + required[date].general + required[date].pickup} /></td>
<td key={date}><PlainDiffCell available={avail[date]} required={required[date].booked + required[date].general + required[date].pickup} warnOverAmber={warnOverAmber} warnUnderAmber={warnUnderAmber} /></td>
))}
<td />
</tr>
@ -948,10 +948,13 @@ function StaffTable({ bookings, staff, required, warnOverRed, warnOverAmber, war
)
}
function PlainDiffCell({ available, required }: { available: number; required: number }) {
function PlainDiffCell({ available, required, warnOverAmber, warnUnderAmber }: {
available: number; required: number; warnOverAmber: number; warnUnderAmber: number
}) {
if (available === 0 && required === 0) return <span style={{ color: 'var(--text-mid)' }}></span>
const diff = available - required
return <span style={{ color: 'var(--text-mid)', fontSize: '0.82rem' }}>{diff >= 0 ? '+' : ''}{fmtH(diff)}</span>
const icon = diff > warnOverAmber ? '⚠' : diff < -warnUnderAmber ? '✗' : '✓'
return <span style={{ color: 'var(--text-mid)', fontSize: '0.82rem' }}>{icon} {diff >= 0 ? '+' : ''}{fmtH(diff)}</span>
}
function DiffCell({ available, required, warnOverRed, warnOverAmber, warnUnderAmber, warnUnderRed }: {