Skip to content

Commit 36b9c19

Browse files
committed
Refactor of TRACON Summary section
1 parent be82a68 commit 36b9c19

7 files changed

Lines changed: 1149 additions & 168 deletions

File tree

README.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,10 @@ Then open [http://localhost:3000](http://localhost:3000).
5050
- `components/tfms-projection-map.js`
5151
- `app/tools/tfms/styles.css`
5252
- `lib/tfms/compute.js`
53+
- `lib/tfms/tracon-volumes.js`
5354
- `data/tfms-sectors.json`
55+
- `data/tfms-tracon-volumes.json`
56+
- `data/tfms-tracon-airports.json`
5457
- `data/tfms-airport-queue-boxes.json`
5558
- `data/tfms-event-splits.json` (currently hidden from UI, retained for future events)
5659

@@ -119,9 +122,13 @@ Default sort priority:
119122

120123
Current cards/modules:
121124
- Specialty Summary (`Now`, `+10`, `+20`, `+30`)
122-
- Online Positions (ZHU enroute + TRACON)
125+
- Online Positions (ZHU enroute controllers)
126+
- TRACON Summary
127+
- Core cards: `I90`, `AUS`, `SAT`, `MSY`
128+
- Core metrics: total airborne, arrivals, departures, overflights
129+
- Airport subcards: queue count, average queue minutes, `TWR Online/Offline`
130+
- Remaining TRACONs: online shown as compact cards; offline consolidated into `TRACONs Offline (N)` hover list
123131
- Enhanced Projection Map
124-
- Departure Queue (`KIAH`, `KHOU`, `KAUS`, `KSAT`, `KMSY`)
125132

126133
Event split summary:
127134
- Logic/data retained
@@ -131,6 +138,10 @@ Queue boxes:
131138
- Config supports `bounds`, `geojson`, or `areas`
132139
- Multiple entries for the same ICAO are merged into one card
133140

141+
TRACON airport mapping:
142+
- `data/tfms-tracon-airports.json` defines airport membership for TRACON-based arrival/departure/overflight classification
143+
- Codes are normalized against internal airport handling in `data/tfms-internal-airports.json`
144+
134145
Projection/summary inclusion logic:
135146
- Flight must pass ZHU relevance checks (in ZHU, near perimeter inbound, or inbound to tracked internal airports)
136147
- Baseline minimum groundspeed filter: `>= 20 kts`

app/tools/tfms/styles.css

Lines changed: 266 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -171,54 +171,184 @@
171171
background: color-mix(in srgb, #ef4444 14%, transparent);
172172
}
173173

174-
.tfms-queue-grid {
174+
.tfms-tracon-core-grid {
175175
display: grid;
176-
gap: 0.6rem;
177-
grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr));
176+
gap: 0.5rem;
177+
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
178178
}
179179

180-
.tfms-queue-card {
180+
.tfms-tracon-core-column {
181+
display: grid;
182+
gap: 0.62rem;
183+
align-content: start;
184+
}
185+
186+
.tfms-tracon-core-card {
181187
border: 1px solid var(--surface-border);
182-
background: color-mix(in srgb, var(--surface-soft) 72%, transparent);
183188
border-radius: 0.7rem;
184-
padding: 0.55rem 0.65rem;
189+
background: color-mix(in srgb, var(--surface-soft) 78%, transparent);
190+
padding: 0.56rem 0.68rem;
191+
box-shadow: 0 0 0 1px color-mix(in srgb, white 7%, transparent);
192+
}
193+
194+
.tfms-tracon-core-header {
195+
display: flex;
196+
align-items: center;
197+
justify-content: space-between;
198+
}
199+
200+
.tfms-tracon-core-header .tfms-tracon-kpi-id {
201+
width: auto;
202+
flex: 0 0 auto;
203+
text-align: left;
204+
}
205+
206+
.tfms-tracon-core-airborne-header-value {
207+
flex: 1 1 auto;
208+
text-align: center;
209+
line-height: 1;
210+
}
211+
212+
.tfms-tracon-core-stat-list {
213+
margin-top: 0.34rem;
214+
display: grid;
215+
gap: 0.16rem;
216+
font-size: 0.75rem;
217+
font-weight: 600;
218+
letter-spacing: 0.03em;
185219
}
186220

187-
.tfms-queue-green {
188-
border-color: color-mix(in srgb, #16a34a 48%, var(--surface-border));
221+
.tfms-tracon-core-stat-row {
222+
display: flex;
223+
align-items: center;
224+
justify-content: space-between;
225+
gap: 0.75rem;
189226
}
190227

191-
.tfms-queue-yellow {
192-
border-color: color-mix(in srgb, #f59e0b 48%, var(--surface-border));
228+
.tfms-tracon-core-stat-row > span:first-child {
229+
color: var(--muted);
230+
font-weight: 600;
231+
}
232+
233+
.tfms-tracon-core-stat-row > span:last-child {
234+
font-weight: 700;
235+
text-align: right;
236+
}
237+
238+
.tfms-tracon-core-metrics-inline {
239+
margin-top: 0.22rem;
240+
display: grid;
241+
grid-template-columns: repeat(3, minmax(0, 1fr));
242+
gap: 0.5rem;
243+
}
244+
245+
.tfms-tracon-core-metric-cell {
246+
display: inline-flex;
247+
align-items: center;
248+
justify-content: flex-start;
249+
gap: 0.22rem;
250+
}
251+
252+
.tfms-tracon-core-metric-cell > span:first-child {
253+
color: var(--muted);
254+
font-size: 0.9rem;
255+
font-weight: 600;
256+
letter-spacing: 0.03em;
257+
line-height: 1;
193258
}
194259

195-
.tfms-queue-red {
196-
border-color: color-mix(in srgb, #ef4444 48%, var(--surface-border));
260+
.tfms-tracon-core-metric-cell > span:last-child {
261+
color: var(--main);
262+
font-weight: 700;
263+
text-align: left;
264+
line-height: 1;
265+
}
266+
267+
.tfms-tracon-core-metric-icon {
268+
display: inline-flex;
269+
align-items: center;
270+
justify-content: center;
271+
opacity: 0.95;
272+
}
273+
274+
.tfms-tracon-core-metric-cell:nth-child(2) > span {
275+
text-align: center;
276+
}
277+
278+
.tfms-tracon-core-metric-cell:nth-child(3) > span {
279+
text-align: right;
280+
}
281+
282+
.tfms-tracon-core-metric-cell:nth-child(2) {
283+
justify-content: center;
284+
}
285+
286+
.tfms-tracon-core-metric-cell:nth-child(3) {
287+
justify-content: flex-end;
288+
}
289+
290+
.tfms-tracon-airport-card {
291+
border: 1px solid color-mix(in srgb, var(--surface-border) 94%, black);
292+
border-radius: 0.55rem;
293+
padding: 0.3rem 0.4rem;
294+
background: color-mix(in srgb, var(--surface-soft) 66%, transparent);
295+
font-size: 0.75rem;
296+
font-weight: 600;
297+
letter-spacing: 0.03em;
298+
}
299+
300+
.tfms-tracon-airport-stack {
301+
display: grid;
302+
gap: 0.32rem;
303+
}
304+
305+
.tfms-tracon-airport-stack-two {
306+
grid-template-columns: repeat(2, minmax(0, 1fr));
307+
}
308+
309+
.tfms-tracon-airport-card-title {
310+
margin: 0;
311+
font-size: 0.75rem;
312+
font-weight: 600;
313+
letter-spacing: 0.03em;
314+
text-transform: none;
315+
color: var(--main);
316+
}
317+
318+
.tfms-tracon-airport-card-header {
319+
display: flex;
320+
align-items: center;
321+
justify-content: space-between;
322+
gap: 0.35rem;
197323
}
198324

199-
.tfms-queue-trend {
325+
.tfms-tracon-airport-card-header .tfms-core-status-pill {
326+
padding: 0.06rem 0.32rem;
327+
font-size: 0.52rem;
328+
}
329+
330+
.tfms-tracon-airport-card .tfms-tracon-core-stat-row {
331+
margin-top: 0.22rem;
332+
}
333+
334+
.tfms-core-status-pill {
200335
border: 1px solid var(--surface-border);
201336
border-radius: 9999px;
202-
padding: 0.08rem 0.45rem;
203-
font-size: 0.62rem;
337+
padding: 0.08rem 0.42rem;
338+
font-size: 0.58rem;
204339
font-weight: 700;
205340
letter-spacing: 0.08em;
206341
text-transform: uppercase;
342+
white-space: nowrap;
207343
}
208344

209-
.tfms-queue-trend-up {
210-
color: #ef4444;
211-
border-color: color-mix(in srgb, #ef4444 55%, var(--surface-border));
212-
background: color-mix(in srgb, #ef4444 18%, var(--surface-soft));
213-
}
214-
215-
.tfms-queue-trend-down {
345+
.tfms-core-status-pill-online {
216346
color: #16a34a;
217347
border-color: color-mix(in srgb, #16a34a 55%, var(--surface-border));
218348
background: color-mix(in srgb, #16a34a 18%, var(--surface-soft));
219349
}
220350

221-
.tfms-queue-trend-flat {
351+
.tfms-core-status-pill-offline {
222352
color: var(--muted);
223353
border-color: var(--surface-border);
224354
background: color-mix(in srgb, var(--surface-soft) 88%, transparent);
@@ -288,6 +418,119 @@
288418
transform: translateY(0);
289419
}
290420

421+
.tfms-tracon-kpi-grid {
422+
display: flex;
423+
flex-wrap: wrap;
424+
gap: 0.4rem;
425+
}
426+
427+
.tfms-tracon-kpi-card {
428+
flex: 0 0 auto;
429+
width: 8.35rem;
430+
border: 1px solid var(--surface-border);
431+
border-radius: 0.65rem;
432+
background: color-mix(in srgb, var(--surface-soft) 72%, transparent);
433+
padding: 0.38rem 0.48rem;
434+
}
435+
436+
.tfms-tracon-kpi-inline {
437+
padding-top: 0.34rem;
438+
padding-bottom: 0.34rem;
439+
}
440+
441+
.tfms-tracon-kpi-inline-row {
442+
display: flex;
443+
align-items: center;
444+
gap: 0.35rem;
445+
min-width: 0;
446+
}
447+
448+
.tfms-tracon-kpi-inline-row-airborne .tfms-tracon-kpi-left {
449+
display: inline-flex;
450+
align-items: center;
451+
gap: 0.35rem;
452+
min-width: 0;
453+
}
454+
455+
.tfms-tracon-kpi-left {
456+
display: inline-flex;
457+
align-items: center;
458+
gap: 0.35rem;
459+
min-width: 0;
460+
}
461+
462+
.tfms-tracon-kpi-inline-row-airborne .tfms-tracon-kpi-id {
463+
width: auto;
464+
flex: 0 0 auto;
465+
text-align: left;
466+
}
467+
468+
.tfms-tracon-kpi-inline-row-airborne .tfms-tracon-kpi-count {
469+
margin-left: auto;
470+
width: auto;
471+
min-width: 1.5ch;
472+
flex: 0 0 auto;
473+
text-align: right;
474+
}
475+
476+
.tfms-tracon-kpi-id {
477+
width: 2.45rem;
478+
flex: 0 0 2.45rem;
479+
text-align: center;
480+
white-space: nowrap;
481+
}
482+
483+
.tfms-tracon-kpi-center {
484+
width: 1.55rem;
485+
flex: 0 0 1.55rem;
486+
text-align: center;
487+
white-space: nowrap;
488+
min-width: 0;
489+
}
490+
491+
.tfms-tracon-kpi-count {
492+
width: 1.55rem;
493+
}
494+
495+
.tfms-tracon-kpi-online {
496+
border-color: color-mix(in srgb, #16a34a 46%, var(--surface-border));
497+
}
498+
499+
.tfms-tracon-kpi-offline {
500+
border-color: var(--surface-border);
501+
}
502+
503+
.tfms-tracon-kpi-offline-summary {
504+
width: auto;
505+
display: inline-flex;
506+
align-items: center;
507+
justify-content: center;
508+
padding: 0.34rem 0.62rem;
509+
color: var(--muted);
510+
font-size: 0.66rem;
511+
font-weight: 700;
512+
letter-spacing: 0.06em;
513+
text-transform: uppercase;
514+
white-space: nowrap;
515+
}
516+
517+
.tfms-tracon-kpi-status {
518+
margin-left: auto;
519+
flex: 0 0 auto;
520+
border: 1px solid var(--surface-border);
521+
border-radius: 9999px;
522+
padding: 0.04rem 0.26rem;
523+
font-size: 0.51rem;
524+
font-weight: 700;
525+
letter-spacing: 0.06em;
526+
text-transform: uppercase;
527+
white-space: nowrap;
528+
}
529+
530+
.tfms-tracon-kpi-dot {
531+
flex: 0 0 auto;
532+
}
533+
291534
:root[data-theme="dark"] .tfms-tracon-chip-online {
292535
border-color: color-mix(in srgb, var(--accent) 60%, var(--surface-border));
293536
background: color-mix(in srgb, var(--accent) 20%, var(--surface-soft));

0 commit comments

Comments
 (0)